【UOJ449】[集訓隊作業2018]喂鴿子

【UOJ449】[集訓隊作業2018]喂鴿子

題面

UOJ

題解

考慮\(\text{min-max}\)容斥,那麼答案爲

\[\sum_{i=1}^n(-1)^{i+1}{n\choose i}f_i \]

其中\(f_i\)表示選出\(i\)只鴿子然後其中第一隻鴿子被餵飽的期望時間。
那麼\(f_i=\sum_{j}p_j\times j\),其中\(p_j\)表示上述過程發生在時刻\(j\)的概率,
這樣子不好統計,我們換一種統計方法:
\(f_i=\sum_jp'_j\)\(p'_j\)表示在\(j\)時刻選出的鴿子沒有一隻餵飽的概率,也就是上述過程發生時刻大於等於\(j+1\)的概率。
再定義一個數組\(g_{c,s}\)表示\(c\)只鴿子單獨拿出來,時刻\(s\)沒有餵飽的的概率。
那麼

\[\begin{aligned} p'_i&=\sum_{j=0}^{i}g_{c,j} {i\choose j}\big(\frac {c}{n}\big)^j\big(\frac {n-c}{n}\big)^{i-j} \end{aligned} \]

也就是說

\[\begin{aligned} f_c&=\sum_{i\geq 1}p'_i\\ &=\sum_{i\geq 1}\sum_{j=0}^ig_{c,j} {i\choose j}\big(\frac {c}{n}\big)^j\big(\frac {n-c}{n}\big)^{i-j}\\ &=\sum_{j=0}^{c(k-1)}g_{c,j}\big(\frac cn\big)^j\sum_{i\geq 0}{i+j\choose i}\big(\frac{n-c}{n}\big)^i \end{aligned} \]

注意到後面那一坨如果令\(x=\frac {n-c}c\)的話,就是\(\sum_{i\geq 0}{i+j\choose j}x^i=(\frac {1}{1-x})^{j+1}=(\frac nc)^{j+1}\)
那麼\(f_c=\frac nc\sum_{j=0}^{c(k-1)} g_{c,j}\)
如果暴力的話可以考慮加入一個鴿子然後dp:
\(g_{c,s}=\sum_{i}g_{c-1,s-i}(\frac 1c)^i(\frac {c-1}{c})^{s-i}{s\choose i}\)
NTT 優化做到總複雜度\(O(n^2k(\log n+\log k))\)

考慮怎麼把這個\(\log\)去掉,令 EGF \(g_c(x)=({\sum_{i=0}^{k} \frac {x^i}{i!}})^c\)(實際上是\(k-1\)這裏爲了方便所以寫成\(k\),下同),那麼

\[g(x)=1+x+\frac {x^2}{2!}+...+\frac {x^k}{k!}\\ g'(x)=g(x)-\frac {x^k}{k!}\\ \begin{aligned} g'_c(x)&=cg_{c-1}(x)\big(g(x)-\frac {x^k}{k!}\big)\\ &=c\big(g_{c}(x)-\frac {x^k}{k!}g_{c-1}(x)\big) \end{aligned}\\ g_{c,s+1}=\frac{c}{s+1}(g_{c,s}-\frac {1}{k!}g_{c-1,s-k}) \]

於是可以遞推了,最後\(g_{c,s}\)乘上一個\(\frac {1}{c^s}\)就是概率了。

代碼

#include <iostream> 
#include <cstdio> 
#include <cstdlib> 
#include <cstring> 
#include <cmath> 
#include <algorithm> 
using namespace std; 
const int Mod = 998244353; 
int fpow(int x, int y) { 
	int res = 1; 
	while (y) {
		if (y & 1) res = 1ll * res * x % Mod; 
		x = 1ll * x * x % Mod; 
		y >>= 1; 
	} 
	return res; 
} 
const int MAX_N = 55, MAX_M = MAX_N * 1005; 
int g[MAX_N][MAX_M], ifc[MAX_M], fac[MAX_M]; 
int f[MAX_N]; 
int N, K; 
int C(int n, int m) { return 1ll * fac[n] * ifc[m] % Mod * ifc[n - m] % Mod; } 
int main () { 
#ifndef ONLINE_JUDGE 
    freopen("cpp.in", "r", stdin); 
#endif 
	cin >> N >> K; 
	for (int i = fac[0] = 1; i <= N * K; i++) fac[i] = 1ll * fac[i - 1] * i % Mod; 
	ifc[N * K] = fpow(fac[N * K], Mod - 2); 
	for (int i = N * K - 1; ~i; i--) ifc[i] = 1ll * ifc[i + 1] * (i + 1) % Mod; 
	for (int i = 0; i < K; i++) g[1][i] = ifc[i]; 
	for (int i = 2; i <= N; i++) {
		g[i][0] = 1; 
		for (int j = 1; j <= i * (K - 1); j++) { 
			g[i][j] = g[i][j - 1]; 
			if (j - K >= 0) g[i][j] = (g[i][j] - 1ll * ifc[K - 1] * g[i - 1][j - K]) % Mod; 
			g[i][j] = (g[i][j] + Mod) % Mod; 
			g[i][j] = 1ll * i * ifc[j] % Mod * fac[j - 1] % Mod * g[i][j] % Mod; 
		} 
	} 
	
	for (int i = 1; i <= N; i++) {
		int t = 1ll * ifc[i] * fac[i - 1] % Mod; 
		for (int pw = 1, j = 0; j <= i * (K - 1); j++, pw = 1ll * pw * t % Mod) 
			g[i][j] = 1ll * g[i][j] * fac[j] % Mod * pw % Mod; 
	} 
	for (int i = 1; i <= N; i++) { 
		for (int j = 0; j <= i * (K - 1); j++) f[i] = (f[i] + g[i][j]) % Mod; 
		f[i] = 1ll * f[i] * N % Mod * ifc[i] % Mod * fac[i - 1] % Mod; 
	} 
	int ans = 0; 
	for (int i = 1; i <= N; i++) { 
		if (i & 1) ans = (ans + 1ll * C(N, i) * f[i]) % Mod; 
		else ans = (ans - 1ll * C(N, i) * f[i]) % Mod, ans = (ans + Mod) % Mod; 
	} 
	printf("%d\n", ans); 
    return 0; 
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章