[PKUWC2018]猎人杀

sol

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

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

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

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

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

[egin{aligned} Pr(S)&=sum_{i=0}^inftyleft(dfrac{tot-sum-w_1}{tot} ight)^i imes dfrac{w_1}{tot}\ &=dfrac{1}{1-dfrac{tot-sum-w_i}{tot}} imesdfrac{w_1}{tot}\ &=dfrac{w_1}{sum+w_1} end{aligned} ]

那么上面的式子就变成

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

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

[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;
}
原文地址:https://www.cnblogs.com/ac-evil/p/12838929.html