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;
}