比较难推式子,对斯特林数和组合数的应用要求比较高。
题意:$m$ 张牌中有一张王牌,重排 $n$ 次,求第一张是王牌的次数 $k$ 次方的期望对 998244353 取模的结果。$k\leq 10 ^ 7, n, m\leq 998244353$。
容易发现单次第一张是王牌的期望显然为 $\dfrac 1m$,令其为 $p$。
则答案可以写作:
看到 $i ^ k$,考虑使用第二类斯特林数展开,让 $k$ 下来:
考虑后面的一坨东西,我们看看能变成什么(此时 $j$ 为常量,我们写为 $a$):
这个式子就比较简洁了,我们带入原来的式子就是:
其实现在完全可以做到 $O(k\log k)$(循环上界肯定不超过 $k$,否则为 0),良心(?)的出题人也给了一定的部分分。但是肯定跑不过 $10 ^ 7$,考虑是否做到线性递推。
容易发现 $\displaystyle {k\brace j}$ 一定是需要 $O(n\log n)$ 的,所以考虑将其砍掉。第二类斯特林数的表达方式可以用二项式反演得到,为:
但是注意这是在 $n\geq m$ 的情况下成立的,而我们枚举的 $\sum_{j = 0} ^ n {k\brace j}$ 可能是不满足的,所以我们分类讨论。
先讨论难一点的,就是 $n > k$ 的情况,这样枚举上界就变为了 $k$。
推到这里,似乎我们的式子已经比较简洁了,但是后面的一坨没法使用二项式定理(上界不同),我们陷入了死胡同。
对后面这个式子硬上,考虑裂项组合数递推,设 $\displaystyle S(t) = \sum_{i = 0} ^ {k - t} (-p) ^ j\binom{n - t}{i}$。考虑大力变形:
边界显然为 $S(k) = 1$,倒序递推即可。注意 $\binom{n - t - 1}{k - t}$ 无法计算,我们可以变形:
同时计算 $S(t)$ 和上升幂并预处理 $p ^ k$,可以做到线性。
现在回来,答案可以表示为:
维护 $n ^ \underline{j}$,即可边计算答案边计算下降幂。至此,我们解决了 $n > k$ 的情况,复杂度 $O(k)$。
下面我们计算 $n\leq k$ 的情况。
还是可以做到线性,可以直接计算 $\binom ni$,复杂度 $O(k)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| void init() { fact[0] = infact[0] = 1; for (int i = 1; i < N; ++ i) fact[i] = (LL) fact[i - 1] * i % Mod; infact[N - 1] = qpow(fact[N - 1]); for (int i = N - 2; i; -- i) infact[i] = (LL) infact[i + 1] * (i + 1) % Mod; pk[1] = 1; for (int i = 2; i < N; ++ i) { if (!st[i]) prime[cnt ++] = i, pk[i] = qpow(i, k); for (int j = 0; j < cnt && i * prime[j] < N; ++ j) { st[i * prime[j]] = true, pk[i * prime[j]] = (LL) pk[i] * pk[prime[j]] % Mod; if (i % prime[j] == 0) break; } } pw[0] = 1; for (int i = 1; i < N; ++ i) pw[i] = (LL) pw[i - 1] * p % Mod; }
int C(int n, int m) { return (LL) fact[n] * infact[m] % Mod * infact[n - m] % Mod; }
namespace NLarge { void solve() { int up = 1; s[k] = 1; for (int t = k - 1; t; -- t) { up = (LL) up * (n - t - 1) % Mod; s[t] = (LL) (Mod + 1 - p) * s[t + 1] % Mod; if ((t ^ k) & 1) adj(s[t] -= (LL) up * infact[k - t] % Mod * pw[k - t] % Mod); else s[t] = (s[t] + (LL) up * infact[k - t] % Mod * pw[k - t]) % Mod; } int dn = 1, res = 0; for (int j = 1; j <= k; ++ j) { dn = (LL) dn * (n - j + 1) % Mod; res = (res + (LL) dn * infact[j] % Mod * pk[j] % Mod * pw[j] % Mod * s[j]) % Mod; } std::cout << res << std::endl; } }
namespace NSmall { void solve() { int res = 0, cur = qpow(Mod + 1 - p, n), inv = qpow(Mod + 1 - p); for (int i = 1; i <= n; ++ i) { cur = (LL) cur * inv % Mod; res = (res + (LL) pw[i] * pk[i] % Mod * C(n, i) % Mod * cur) % Mod; } std::cout << res << std::endl; } }
int main() { std::cin >> n >> p >> k, p = qpow(p); init(); if (n > k) NLarge::solve(); else NSmall::solve(); return 0; }
|