[ICPC2020上海E] The Journey of Geor Autumn - 组合数学
Description
对于给定的 (n,k le 10^7),求有多少个满足这样条件的 (1..n) 全排列:对于任意 (i >k),(a_i > min_{j in [i-k,i)} a_j).
Solution
最小的数必须放在 ([1,k]) 中的任意位置 (pos) 上。一旦这个数确定,那么 ([1,pos)) 这段位置区间中可以任意填数,((pos,n]) 这一段构成一个递归的子问题。
设 (f[n]) 表示对于一个长度为 (n) 的序列的答案,则
[f[n] = sum_{j=1}^{min(i,k)} inom{i-1}{j-1} f(i-j) (j-1)!
]
变形得到
[frac {f(i)} {i!} = frac 1 i sum_{j=1}^{min(i,k)} frac{f(i-j)}{(i-j)!}
]
前缀和优化一下即可。
阶乘辅助是线性预处理逆元的简单而有效的手段。
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;
const int N = 1e+7 + 5;
int s[N], inv[N], ifac[N], fac[N], n, k;
int qpow(int p, int q)
{
return (q & 1 ? p : 1) * (q ? qpow(p * p % mod, q / 2) : 1) % mod;
}
signed main()
{
ios::sync_with_stdio(false);
cin >> n >> k;
fac[0] = 1;
for (int i = 1; i <= n + 1; i++)
fac[i] = fac[i - 1] * i % mod;
ifac[n + 1] = qpow(fac[n + 1], mod - 2);
for (int i = n; i >= 0; i--)
ifac[i] = ifac[i + 1] * (i + 1) % mod;
for (int i = 1; i <= n; i++)
inv[i] = ifac[i] * fac[i - 1] % mod;
s[0] = 1;
for (int i = 1; i <= n; i++)
s[i] = (s[i - 1] + (s[i - 1] - (i - k - 1 < 0 ? 0 : s[i - k - 1]) + mod) % mod * inv[i] % mod) % mod;
cout << (s[n] - s[n - 1] + mod) % mod * fac[n] % mod << endl;
}