zoukankan      html  css  js  c++  java
  • P5644-[PKUWC2018]猎人杀【NTT,分治】

    正题

    题目链接:https://www.luogu.com.cn/problem/P5644


    题目大意

    (n)个人,每个人被选中的权重是(a_i)。每次按照权重选择一个没有死掉的人杀死,求第(1)个人最后死的概率。输出答案对(998244353)取模。

    (w_i>0,sum_{i=1}^nw_ileq 10^5)


    解题思路

    这个死掉之后概率的分母会变所以挺麻烦的,考虑一下变成每次随便选择一个人,如果没有死就杀掉,这样每个人被选择的概率就不变了。

    然后考虑到计算恰好最后一个死很麻烦,可以假设第(1)个人死之后至少还剩下集合(T)的人,然后容斥这样就不需要考虑到剩下的人必须在前面都选过一次了。

    (P(T))表示(1)死之后剩下集合(T)的人的概率,怎么求这个东西,我们可以枚举一下杀到(1)之前的轮数(记(S)为全集,(W(S)=sum_{xin S}w_x)

    [P(T)=sum_{i=0}^{infty}(frac{W(S)-W(T)-w_1}{W(S)})^ifrac{w_1}{W(S)} ]

    等比数列求和展开一下就是

    [P(T)=frac{(frac{W(S)-W(T)-w_1}{W(S)})^infty-1}{frac{W(S)-W(T)-w_1}{W(S)}-1}frac{w_1}{W(S)} ]

    然后因为那个(infty)的东西是收敛(也就是等于(0))的所以

    [P(T)=frac{W(S)}{W(T)+w_1}frac{w_1}{W(S)}=frac{w_1}{w_1+W(T)} ]

    就好了

    然后答案就是

    [sum_{Tin S}(-1)^{|T|}P(T)=sum_{Tin S}(-1)^{|T|}frac{w_1}{w_1+W(T)} ]

    但是这样的复杂度是(O(2^n))的显然不可能过。

    但是我们不难发现的是因为(W(S)leq 10^5),所以我们可以设(f(i))表示对于所有集合(T)使得(W(T)=i)的容斥系数和那么答案就变成了

    [sum_{i=0}^{W(S)}f(i)frac{w_1}{w_1+i} ]

    但是这个(f)怎么求,其实看上去就很生成函数,(f(i))相等于(prod_{i=2}^n(1-x^{w_i}))的第(i)次项系数。

    这个东西我们分治+(NTT)求就好了(因为这个做法实际上和分治(NTT)有区别)

    时间复杂度(O(mlog^2 m))(m=W(S))


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=4e5+10,P=998244353;
    ll n,w[N],r[N],x[N],y[N];
    struct Poly{
    	ll n,a[N];
    }F[20];bool v[20];
    ll power(ll x,ll b){
    	ll ans=1;
    	while(b){
    		if(b&1)ans=ans*x%P;
    		x=x*x%P;b>>=1;
    	}
    	return ans;
    }
    void NTT(ll *f,ll n,ll op){
    	for(ll i=0;i<n;i++)
    		if(i<r[i])swap(f[i],f[r[i]]);
    	for(ll p=2;p<=n;p<<=1){
    		ll len=p>>1,tmp=power(3,(P-1)/p);
    		if(op==-1)tmp=power(tmp,P-2);
    		for(ll k=0;k<n;k+=p){
    			ll buf=1;
    			for(ll i=k;i<k+len;i++){
    				ll tt=f[i+len]*buf%P;
    				f[i+len]=(f[i]-tt+P)%P;
    				f[i]=(f[i]+tt)%P;
    				buf=buf*tmp%P;
    			}
    		}
    	}
    	if(op==-1){
    		ll inv=power(n,P-2);
    		for(ll i=0;i<n;i++)
    			f[i]=f[i]*inv%P;
    	}
    	return;
    }
    void Mul(Poly &a,Poly &b){
    	for(ll i=0;i<a.n;i++)x[i]=a.a[i];
    	for(ll i=0;i<b.n;i++)y[i]=b.a[i];
    	ll l=1;while(l<a.n+b.n)l<<=1;
    	for(ll i=0;i<l;i++)r[i]=(r[i>>1]>>1)|((i&1)?(l>>1):0);
    	for(ll i=a.n;i<l;i++)x[i]=0;
    	for(ll i=b.n;i<l;i++)y[i]=0;
    	NTT(x,l,1);NTT(y,l,1);
    	for(ll i=0;i<l;i++)x[i]=x[i]*y[i]%P;
    	NTT(x,l,-1);
    	for(ll i=0;i<l;i++)a.a[i]=x[i];
    	a.n=a.n+b.n-1;return;
    }
    ll findq(){
    	for(ll i=0;i<20;i++)
    		if(!v[i]){v[i]=1;return i;}
    }
    ll Solve(ll l,ll r){
    	if(l==r){
    		ll p=findq();
    		for(ll i=0;i<=w[l];i++)
    			F[p].a[i]=0;
    		F[p].a[0]=1;F[p].a[w[l]]=P-1;
    		F[p].n=w[l]+1;return p;
    	}
    	ll mid=(l+r)>>1;
    	ll ls=Solve(l,mid),rs=Solve(mid+1,r);
    	Mul(F[ls],F[rs]);v[rs]=0;return ls;
    }
    signed main()
    {
    	scanf("%lld",&n);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld",&w[i]);
    	ll p=Solve(2,n),ans=0;
    	for(ll i=0;i<F[p].n;i++)
    		(ans+=F[p].a[i]*w[1]%P*power(w[1]+i,P-2)%P)%=P;
    	printf("%lld
    ",(ans+P)%P);
    	return 0;
    }
    
  • 相关阅读:
    HDU 3681 Prison Break 越狱(状压DP,变形)
    POJ 2411 Mondriaan's Dream (状压DP,骨牌覆盖,经典)
    ZOJ 3471 Most Powerful (状压DP,经典)
    POJ 2288 Islands and Bridges (状压DP,变形)
    HDU 3001 Travelling (状压DP,3进制)
    POJ 3311 Hie with the Pie (状压DP)
    POJ 1185 炮兵阵地 (状压DP,轮廓线DP)
    FZU 2204 7
    POJ 3254 Corn Fields (状压DP,轮廓线DP)
    ZOJ 3494 BCD Code (数位DP,AC自动机)
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14898561.html
Copyright © 2011-2022 走看看