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...n的全排列中,i>k时,ai>minj∈[i−k,i)aj。(n≥107k≥107)
思路
[ 1 , n ] 的 区 间 里 , 因 为 i > k 时 才 会 有 要 求 , 所 以 将 最 小 的 数 放 在 [ 1 , k ] 当 中 。 [1,n]的区间里,因为i>k时才会有要求,所以将最小的数放在[1,k]当中。 [1,n]的区间里,因为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)的数随便放,有Cn−1pos−1∗(pos−1)!种方案
- ( p o s , n ] 的 方 法 就 又 要 要 求 了 , 假 设 方 案 数 为 f [ n − p o s ] (pos,n]的方法就又要要求了,假设方案数为f[n-pos] (pos,n]的方法就又要要求了,假设方案数为f[n−pos]
所 以 我 们 得 出 所以我们得出 所以我们得出
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=1∑min(n,k)Cn−1j−1∗(j−1)!∗f[n−j]
这
样
我
们
就
要
递
归
出
子
问
题
了
,
即
:
这样我们就要递归出子问题了,即:
这样我们就要递归出子问题了,即:
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=1∑min(i,k)Ci−1j−1∗(j−1)!∗f[i−j]
所 以 暴 力 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]=(i−1)!∗j=1∑min(i,k)(i−j)!f[i−j]
可 以 看 到 , 等 式 右 边 就 是 一 个 前 缀 和 , 所 以 转 移 的 过 程 中 维 护 前 缀 和 即 可 。 可以看到,等式右边就是一个前缀和,所以转移的过程中维护前缀和即可。 可以看到,等式右边就是一个前缀和,所以转移的过程中维护前缀和即可。
所 以 我 们 先 预 处 理 阶 乘 和 阶 乘 逆 元 , 在 前 缀 和 优 化 即 可 。 所以我们先预处理阶乘和阶乘逆元,在前缀和优化即可。 所以我们先预处理阶乘和阶乘逆元,在前缀和优化即可。
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();
}