P5644 [PKUWC2018] 猎人杀 题解

首先可以发现从活着的猎人中选和从全部猎人中选是等价的,不影响最终的概率。那么就可以转化为求猎人 \(2\sim n\) 都在猎人一第一次被选中之前被选过的概率。

考虑容斥,枚举一个子集 \(S\) 使得 \(S\) 中的猎人都没在猎人一之前被选过,那么易得对答案的贡献就是 \((-1)^{|S|}\times\frac{1}{1-\frac{\sum _{i\in S}w_i}{\sum w}}\times\frac{w_1}{\sum w}\)

发现 \(\sum w\le 10^5\),考虑生成函数,直接 NTT 求出 \(\sum_{i\in S}w_i\) 的每种情况的容斥系数的和即可。可以建个优先队列,把每个猎人的生成函数丢进去,每次拿出次数最小的两个合并即可,可以通过此题。

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 100003
#define md 998244353
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
using namespace std;
int n,s,k,rev[524288];
ll xi,ans,a[mxn],f[524288],g[524288];
ll power(ll x,int y){
	ll ans=1;
	for(;y;y>>=1){
		if(y&1)ans=ans*x%md;
		x=x*x%md;
	}
	return ans;
}
void ntt(ll *a,int n,int flag){
	rept(i,0,n)if(i<rev[i])swap(a[i],a[rev[i]]);
	for(int h=1;h<n;h<<=1){
		ll x,y,s=power(3,499122176/h);
		for(int j=0;j<n;j+=h<<1){
			ll w=1;
			for(int k=j;k<j+h;++k){
				x=a[k],y=w*a[k+h]%md;
				a[k]=(x+y)%md;
				a[k+h]=(x-y+md)%md;
				w=w*s%md;
			}
		}
	}
	if(flag==-1){
		ll p=power(n,md-2);
		reverse(a+1,a+n);
		rept(i,0,n)a[i]=a[i]*p%md;
	}
}
struct node{
	vector<int>a;
	node operator*(node x){
		node ans;
		for(k=0,s=1;s<a.size()+x.a.size()-1;s<<=1,++k);
		rept(i,0,s)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1)),f[i]=g[i]=0;
		rept(i,0,a.size())f[i]=a[i];
		rept(i,0,x.a.size())g[i]=x.a[i];
		ntt(f,s,1);ntt(g,s,1);
		rept(i,0,s)f[i]=f[i]*g[i]%md;
		ntt(f,s,-1);
		ans.a.resize(a.size()+x.a.size()-1);
		rept(i,0,ans.a.size())ans.a[i]=f[i];
		return ans;
	}
};
inline bool operator<(node x,node y){
	return x.a.size()>y.a.size();
}
priority_queue<node>q;
signed main(){
	scanf("%d",&n);
	if(n==1){
		puts("1");
		return 0;
	}
	rep(i,1,n){
		scanf("%lld",&a[i]),xi=(xi+a[i])%md;
		if(i!=1){
			node e;
			e.a.resize(a[i]+1,0);
			e.a[0]=md-1;
			e.a[a[i]]=1;
			q.push(e);
		}
	}
	xi=power(xi,md-2);
	while(q.size()>1){
		node x=q.top();q.pop();
		node y=q.top();q.pop();
		q.push(x*y);
	}
	node s=q.top();
	rept(i,0,s.a.size())if(s.a[i]){
		ans=(ans+power((1-i*xi)%md,md-2)*s.a[i]%md*xi%md*a[1])%md;
	}
	cout<<(ans+md)%md;
	return 0;
}
posted @ 2024-05-08 16:29  zifanwang  阅读(12)  评论(0编辑  收藏  举报