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;
}