[PKUWC2018]猎人杀

sol

一道容斥题,要结合生成函数。

首先有个技巧:设猎人\(i\)还活着,他被开枪的概率为剩下猎人中按权值分配的概率,这样很麻烦,还需要知道剩余哪些人,其实等价于人都在,但被开枪到死人时要重新开枪的概率。因为对于活着的猎人,被开枪仍然概率不变。这点很重要,因为仍然可以用\(p_i=\dfrac{w_i}{tot}\)表示概率,其中\(tot=\sum\limits_j w_j\)

直接算出来\(1\)号猎人最后挂掉的概率很麻烦,考虑容斥。设集合\(S\subset\{2,3,\dots,n\}\)表示猎人编号的集合,则可以容斥得到

\[ans=1+\sum_{S}(-1)^{\mid S\mid}Pr(S) \]

\(Pr(S)\)表示挂在\(1\)号猎人后面至少为\(S\)中的猎人的概率。现在来分析这个东西,那么显然前面若干次一定是\(\overline S\)中的人,除了\(1\)号被淘汰时立刻停止,否则继续。显然是一个无穷级数的求和。记\(tot=\sum w_i\)\(sum=\sum_{i\in S}w_i\),那么有

\[\begin{aligned} Pr(S)&=\sum_{i=0}^\infty\left(\dfrac{tot-sum-w_1}{tot}\right)^i\times \dfrac{w_1}{tot}\\ &=\dfrac{1}{1-\dfrac{tot-sum-w_i}{tot}}\times\dfrac{w_1}{tot}\\ &=\dfrac{w_1}{sum+w_1} \end{aligned} \]

那么上面的式子就变成

\[ans=1+\sum_S(-1)^{\mid S\mid}\dfrac{w_1}{tot+w_1} \]

根据数据范围提示,转变枚举方式,考虑枚举\(tot\),这个与\(S\)有关,记\(f(i)=\sum_{tot=i}(-1)^{\mid S\mid}\),那么式子就变成了

\[ans=1+\sum_{i}f(i)\dfrac{w_1}{i+w_1} \]

问题就变成了求解\(f\)。使用生成函数构造出\(f\),令\(F=\prod(1-x^{w_i})\),则\(f(i)=[x^i]F(x)\)。至此问题得解。

#include <bits/stdc++.h>
using std::swap; using std::reverse;
typedef std::vector<int> Poly;
const int N = 100005, P = 998244353;
int n, w[N], L = 0;
int inc(int a, int b) { return (a += b) >= P ? a-P : a; }
int qpow(int a, int b) {
	int t = 1;
	for (; b; b >>= 1, a = 1LL*a*a%P)
		if (b & 1) t = 1LL*t*a%P;
	return t;
}
int W[N*2];
void prework(int n) {
	for (int i = 1; i < n; i <<= 1)
		for (int j = W[i] = 1, Wn = qpow(3, (P-1)/i>>1); j < i; j++)
			W[i+j] = 1LL*W[i+j-1]*Wn%P;
}
void fft(Poly &a, int n, int op) {
	a.resize(n);
	static int rev[N*2];
	for (int i = 1; i < n; i++)
		if ((rev[i] = rev[i>>1]>>1|(i&1?n>>1:0)) > i) swap(a[i], a[rev[i]]);
	for (int q = 1; q < n; q <<= 1)
		for (int p = 0; p < n; p += q<<1)
			for (int i = 0, t; i < q; i++)
				t = 1LL*W[q+i]*a[p+q+i]%P, a[p+q+i] = inc(a[p+i], P-t), a[p+i] = inc(a[p+i], t);
	if (op) return;
	for (int i = 0, inv = qpow(n, P-2); i < n; i++) a[i] = 1LL*a[i]*inv%P;
	reverse(a.begin()+1, a.end());
}
void fix(Poly &A) { int x = A.size(); while (x > 1 && !A[x-1]) x--; A.resize(x); }
int getsz(int n) { int x = 1; while (x < n) x <<= 1; return x; }
Poly operator * (Poly A, Poly B) {
	int L = getsz(A.size() + B.size() - 1);
	fft(A, L, 1), fft(B, L, 1);
	for (int i = 0; i < L; i++) A[i] = 1LL*A[i]*B[i]%P;
	return fft(A, L, 0), fix(A), A;
}
Poly A[N*4]; int ans = 0;
void build(int o, int l, int r) {
	if (l == r) {
		A[o] = Poly(w[l]+1); A[o][0] = P-1; A[o][w[l]] = 1;
		return;
	}
	int mid = l+r>>1;
	build(o<<1, l, mid), build(o<<1|1, mid+1, r);
	A[o] = A[o<<1] * A[o<<1|1];
}
int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &w[i]), L += w[i];
	prework(L);
	build(1, 2, n);
	for (int i = 0; i < A[1].size(); i++)
		ans = (ans + 1LL*A[1][i]*L%P*qpow(L-i, P-2)) % P;
	ans = 1LL*ans*w[1]%P*qpow(L, P-2)%P;
	printf("%d", ans);
	return 0;
}
posted @ 2020-05-06 21:09  AC-Evil  阅读(312)  评论(0编辑  收藏  举报