2020ICPC上海站 E题 The Journey of Geor Autumn 组合数学 + dp

2020ICPC上海站 E题 The Journey of Geor Autumn 组合数学 + dp


传送门: https://ac.nowcoder.com/acm/contest/9925/E

题意

有 多 少 个 满 足 1... n 的 全 排 列 中 , i > k 时 , a i > m i n j ∈ [ i − k , i ) a j 。 ( n ≥ 1 0 7    k ≥ 1 0 7 ) 有多少个满足1...n的全排列中,i > k时,a_i>min_{j\in[i-k,i)}a_j。(n\ge10^7\;k\ge10^7) 1...ni>kai>minj[ik,i)aj(n107k107)

思路

[ 1 , n ] 的 区 间 里 , 因 为 i > k 时 才 会 有 要 求 , 所 以 将 最 小 的 数 放 在 [ 1 , k ] 当 中 。 [1,n]的区间里,因为i>k时才会有要求,所以将最小的数放在[1,k]当中。 [1n]i>k[1,k]

设 位 置 为 p o s , 则 设位置为pos,则 pos

  • [ 1 , p o s ) 的 数 随 便 放 , 有 C n − 1 p o s − 1 ∗ ( p o s − 1 ) ! 种 方 案 [1,pos)的数随便放,有C_{n-1}^{pos-1}*(pos-1)!种方案 [1,pos)便Cn1pos1(pos1)!
  • ( p o s , n ] 的 方 法 就 又 要 要 求 了 , 假 设 方 案 数 为 f [ n − p o s ] (pos,n]的方法就又要要求了,假设方案数为f[n-pos] (pos,n]f[npos]

所 以 我 们 得 出 所以我们得出

f [ n ] = ∑ j = 1 m i n ( n , k ) C n − 1 j − 1 ∗ ( j − 1 ) ! ∗ f [ n − j ] f[n]=\sum_{j=1}^{min(n,k)}C_{n-1}^{j-1}*(j-1)!*f[n-j] f[n]=j=1min(n,k)Cn1j1(j1)!f[nj]
这 样 我 们 就 要 递 归 出 子 问 题 了 , 即 : 这样我们就要递归出子问题了,即:

f [ i ] = ∑ j = 1 m i n ( i , k ) C i − 1 j − 1 ∗ ( j − 1 ) ! ∗ f [ i − j ] f[i]=\sum_{j=1}^{min(i,k)}C_{i-1}^{j-1}*(j-1)!*f[i-j] f[i]=j=1min(i,k)Ci1j1(j1)!f[ij]

所 以 暴 力 O ( n k ) 的 状 态 转 移 的 话 , 就 应 该 这 样 : 所以暴力O(nk)的状态转移的话,就应该这样: O(nk)

void zhuanyi() {
    for(int i = 1;i <= n; i++) {
        for(int j = 1;j <= min(i, k); j++) {
            f[i] += f[i - j] * C(i - 1, j - 1) * fac[j - 1];
        }
    }
}

但 是 O ( n k ) 是 1 e 14 的 复 杂 度 , 这 远 远 超 标 了 , 所 以 我 们 要 优 化 以 下 , 将 转 移 方 程 优 化 为 : 但是O(nk)是1e14的复杂度,这远远超标了,所以我们要优化以下,将转移方程优化为: O(nk)1e14

f [ i ] = ( i − 1 ) ! ∗ ∑ j = 1 m i n ( i , k ) f [ i − j ] ( i − j ) ! f[i]=(i-1)!*\sum_{j=1}^{min(i,k)}\frac{f[i-j]}{(i-j)!} f[i]=(i1)!j=1min(i,k)(ij)!f[ij]

可 以 看 到 , 等 式 右 边 就 是 一 个 前 缀 和 , 所 以 转 移 的 过 程 中 维 护 前 缀 和 即 可 。 可以看到,等式右边就是一个前缀和,所以转移的过程中维护前缀和即可。

所 以 我 们 先 预 处 理 阶 乘 和 阶 乘 逆 元 , 在 前 缀 和 优 化 即 可 。 所以我们先预处理阶乘和阶乘逆元,在前缀和优化即可。

Code(345MS)


#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<int, int> pdd;

#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define mem(a, b) memset(a , b , sizeof(a))
#define FOR(i, x, n) for(int i = x;i <= n; i++)

 const ll mod = 998244353;
// const ll mod = 1e9 + 7;
// const double eps = 1e-6;
// const double PI = acos(-1);
// const double R = 0.57721566490153286060651209;

const int N = 1e7 + 10;

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans % mod;
}

ll fac[N], invfac[N];

void init() {
    fac[0] = 1;
    for(int i = 1;i < N; i++) {
        fac[i] = fac[i - 1] * i % mod;
    }
    invfac[N - 1] = quick_pow(fac[N - 1], mod - 2);
    for(int i = N - 1;i >= 1; i--) {
        invfac[i - 1] = invfac[i] * i % mod;
   }
}

ll f[N];
ll sum[N];

void solve() {
    init();
    int n, k;
     cin >> n >> k;
     f[0] = sum[0] = 1;
     for(int i = 1;i <= n; i++) {
         f[i] = (sum[i - 1] - (i - k - 1 >= 0 ? sum[i - k - 1] : 0)) * fac[i - 1] % mod;
         sum[i] = (sum[i - 1] + f[i] * invfac[i] % mod) % mod;
     }
     cout << (f[n] % mod + mod) % mod << endl;
}

signed main() {
    solve();
}


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值