Legend
有 (n) 只鸽子,每秒钟你随机选一只鸽子喂给它 (1) 粒玉米,当一只鸽子吃到 (k) 粒玉米时会饱,求所有鸽子饱的期望时间。
(0 le n le 50),(0 le k le 1000)。模数 (998244353)。
Editorial
不难发现可以 ( extrm{min-max}) 容斥。
设 (F_i) 为选中 (i) 只鸽子的子集,并给所有 (n) 只鸽子喂玉米,期望喂多少次玉米后,在该子集中第一次出现被喂饱的鸽子。
根据 ( extrm{min-max}) 容斥,注意到我们不关心子集的具体构成,只关心大小,所以答案即为:
[sum_{i=1}^{n}inom{n}{i}(-1)^{i+1}F_i
]
现在问题只有,怎么求 (F_i)。求期望不好求,我们先求方案数,设 (G_{i,j}) 表示喂 (i) 只鸽子玉米,喂了 (j) 次还没有任何鸽子被喂饱的方案数。
(G) 数组直接背包复杂度高达 (O(n^3k)),过不了,不过可以很容易用 ( extrm{EGF})(指数型生成函数)优化到 (O(n^2klog nk)),但这依赖于本题的特殊模数((998244353))。下介绍一种不用 ( extrm{NTT}) 的方法——补集转化。
我们考虑用所有方案减去不合法的方案,可以得到如下转移方程:
[G_{i,j}=i sum_{k=0}^{j-1} G_{i,j-1}-inom{j-1}{k-1}G_{i-1,j-k}
]
其含义可以这样解释:
- 每次我们考虑新增一粒玉米,喂给 (i) 只鸽子中的任意一只。
- 这样子会出现不合法情况,即某一只鸽子之前没饱,被喂了之后就饱了。枚举一下这只被喂饱的鸽子是谁,和它是在哪几次被喂过玉米,算出方案减去。
然后我们就可以通过 (O(n^2k)) 的时间算出 (G)(方案数)。
那么怎么求期望时间 (F) 呢?
[F_i = frac{n}{i} imes isum_{j=0}^{(i-1)(k-1)}(j+k) frac{inom{j+k-1}{k-1}G_{i-1,j}}{i^{j+k}}
]
其含义就是枚举除了饱了的鸽子,其它的鸽子一共吃了多少,然后按期望的定义算就行了。前面乘一个 (frac{n}{i}) 是因为在 (n) 只鸽子内随机时,你只有 (frac{i}{n}) 的概率选到这个子集的鸽子。
然后就可以毫无压力地做这个题了。
Code
#include <bits/stdc++.h>
#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)
freopen(#x".in" ,"r" ,stdin);
freopen(#x".out" ,"w" ,stdout)
#define LL long long
const int MX = 50 + 2;
const LL MOD = 998244353;
const LL phi = MOD - 1;
int read(){
char k = getchar(); int x = 0;
while(k < '0' || k > '9') k = getchar();
while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
return x;
}
LL qpow(LL a ,LL b ,LL p = MOD){
LL ans = 1;
while(b){if(b & 1) ans = ans * a % p;
a = a * a % p ,b >>= 1;
}return ans;
}
int n ,k;
LL dp[MX][50 * 999 + 2] ,S[MX][50 * 999 + 2] ,F[MX];
// dp[i][j] 表示给 i 只不同的鸽子喂 j 粒玉米且都没饱的方案数
// F[i] 表示给 i 只鸽子喂饱第一只的期望时间
LL C(int n ,int m);
LL fac[50000 + 23] ,inv[50000 + 23];
void init(){
fac[0] = 1 ,inv[0] = inv[1] = 1;
for(int i = 1 ; i <= 50000 ; ++i) fac[i] = fac[i - 1] * i % MOD;
for(int i = 2 ; i <= 50000 ; ++i) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
for(int i = 2 ; i <= 50000 ; ++i) inv[i] = inv[i] * inv[i - 1] % MOD;
dp[0][0] = 1;
for(int i = 1 ; i <= n ; ++i){
dp[i][0] = 1;
for(int j = 1 ; j <= (k - 1) * i + 1 ; ++j){
dp[i][j] = i * (dp[i][j - 1] - C(j - 1 ,k - 1) % MOD * (j - k >= 0 ? dp[i - 1][j - k] : 0) % MOD + MOD) % MOD;
}
}
for(int i = 1 ; i <= n ; ++i){ // 枚举被喂了的总鸽子数量
F[i] = 0;
for(int j = 0 ; j <= (k - 1) * (i - 1) ; ++j){ // 枚举其它鸽子吃了多少
F[i] = (F[i] + C(j + k - 1 ,k - 1) * (j + k) % MOD * dp[i - 1][j] % MOD * qpow(i ,(j + k) * (MOD - 2) % phi)) % MOD;
}
// F[i] = F[i] * i % MOD;
}
}
int main(){
n = read() ,k = read();
init();
LL ans = 0;
for(int i = 1 ; i <= n ; ++i){
ans += (i & 1 ? 1 : -1) * F[i] * C(n ,i) % MOD * n % MOD/* * qpow(i ,MOD - 2) % MOD*/;
}
ans = (ans % MOD + MOD) % MOD;
printf("%lld
" ,ans);
return 0;
}
LL C(int n ,int m){return m > n || m < 0 ? 0 : fac[n] * inv[m] % MOD * inv[n - m] % MOD;}