UR 19 通用测评号 题解

First Post:

Last Update:

对于这种结束条件依赖于“每个物品是否达到要求” 的题目,可以枚举最后一次操作在哪里来进行统计。

当然上面那句话讲的是较一般的做法,这道题有更巧妙的方法。

因为这题跟操作次数无关,可以仿照 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;
}