对于这种结束条件依赖于“每个物品是否达到要求”
的题目,可以枚举最后一次操作在哪里来进行统计。
当然上面那句话讲的是较一般的做法,这道题有更巧妙的方法。
因为这题跟操作次数无关,可以仿照 PKUSC 2018 猎人杀
中的方法,将“选择未满舱” 的限制忽略掉。毕竟选了满舱也不会产生影响。
注意到每个舱满的概率都是相同的,所以我们可以算出 号舱满的概率,最后再乘以 。
那么设 表示 号舱满后没有半满 的舱集合,要求 。
要求恰好为
还是非常困难的,我们不妨容斥,钦定 为 号舱满后没有半满的舱集合, 之外的部分有没有半满无所谓。那么对于
,每个 的子集 都会把它算一遍,给 的答案带个 的系数就能让每个 被算到恰好一次。
对于一个集合
怎么算钦定它的答案呢?此时我们只需关心这 个元素在
号舱满之前是怎么操作的。设 。那么枚举 中元素的总操作次数 。可以得到 对答案的贡献为 。其中 的意义就是把那
次操作分给每个元素,定好每个舱做几次。因为操作是有标号的,所以使用 EGF
卷积。后面的分数是除以总方案数得到概率。
现在的问题就是
的各项系数怎么算。 FFT 可以但不优美。考虑建立微分方程来整式递推。
容易发现 。
同时有 。
设 。利用上面等式可以得到:
。
然后就可以递推算出
的各项系数,最后求答案即可。
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
| #include <bits/stdc++.h> using namespace std; const int N = 3e2 + 5,M = 1e5 + 5,P = 998244353; inline void Plus(int &x,const int &y) { x += y;if(x >= P) x -= P;} inline int qpow(int a,int b) { int res = 1;while(b) { if(b&1) res = 1ll * res * a % P;a = 1ll * a * a % P;b >>= 1;} return res;} int n,a,b; int B[N][M]; int fac[M],ifac[M],inv[M]; inline void initfac(int n) { fac[0] = 1; for(int i = 1;i <= n;i++) fac[i] = 1ll * fac[i - 1] * i % P; inv[1] = 1; for(int i = 2;i <= n;i++) inv[i] = 1ll * inv[P % i] * (P - P / i) % P; ifac[0] = 1; for(int i = 1;i <= n;i++) ifac[i] = 1ll * ifac[i - 1] * inv[i] % P; } inline int C(int n,int m) { if(n < 0 || m < 0 || n < m) return 0; return 1ll * fac[n] * ifac[m] % P * ifac[n - m] % P;} int main() { cin >> n >> a >> b; initfac(n * (a + 1)); for(int i = 0;i <= n;i++) B[i][0] = 1; for(int i = 1;i < b;i++) B[1][i] = ifac[i]; for(int c = 2;c <= n;c++) { for(int s = 0;s < c * (b - 1);s++) B[c][s + 1] = 1ll * c * inv[s + 1] % P * (B[c][s] + P - (s >= b - 1 ? 1ll * ifac[b - 1] * B[c - 1][s - b + 1] % P: 0)) % P; } int ans = 0; for(int i = 1;i < n;i++) { int sum = 0; for(int j = 0;j <= i * (b - 1);j++) { int coef = 1ll * B[i][j] * ifac[a - 1] % P; coef = 1ll * coef * fac[j + (a - 1)] % P * qpow(inv[i + 1],j + a) % P; Plus(sum,coef); } sum = 1ll * sum * C(n - 1,i) % P; if(i & 1) Plus(ans,sum); else Plus(ans,P - sum); } ans = 1ll * ans * n % P; cout << ans << endl; return 0; }
|