Loj #2541「PKUWC2018」猎人杀

Loj #2541. 「PKUWC2018」猎人杀

题目链接

好巧妙的题!

游戏过程中,概率的分母一直在变化,所以就非常的不可做。

所以我们将问题转化一下:我们可以重复选择相同的猎人,只不过在一个猎人被选择了过后我们就给他打上标记,再次选择他的时候就无效。这样与原问题是等价的。

证明:

\(sum=\sum_iw_i,kill=\sum_{i被杀死了}w_i\)

攻击到未被杀死的猎人\(i\)的概率为\(P\)

则根据题意\(P=\frac{w_i}{sum-kill}\)

问题转化后:
\[ \\P=\frac{kill}{sum}P+\frac{w_i}{sum}\\ \Rightarrow P=\frac{w_i}{sum-kill}。 \]
然后我们考虑容斥:枚举集合\(T\)中的猎人一定在\(1\)之后被杀死,其他猎人随意。

我们设\(S=\sum_{i\in T}w_i\)

则:
\[ \displaystyle \begin{align} ans&=(-1)^{|T|}\sum_{i=0}^{\infty}(1-\frac{S+w_1}{sum})^i\frac{w_1}{sum} \\&=(-1)^{|T|}\frac{1}{1-(1-\frac{S+w_1}{sum})}\frac{w_1}{sum} \\&=(-1)^{|T|}\frac{w_1}{w_1+S} \end{align} \]
然后我们就可以用背包背出所有\(\sum w_i\)恰好为\(S\)的带上容斥系数的方案数。

但复杂度有点高,于是我们考虑用生成函数来优化。这道题的生成函数还是比较简单,就是\(\Pi (1-x^{w_i})\)。用分治\(NTT\)实现。

代码:

#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 100005

using namespace std;
inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;}

ll ksm(ll t,ll x)  {
    ll ans=1;
    for(;x;x>>=1,t=t*t%mod)
        if(x&1) ans=ans*t%mod;
    return ans;
}

int rev[N<<2];
void NTT(ll *a,int d,int flag) {
    static ll G=3;
    int n=1<<d;
    for(int i=0;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<d-1);
    for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    for(int s=1;s<=d;s++) {
        int len=1<<s,mid=len>>1;
        ll w=flag==1?ksm(G,(mod-1)/len):ksm(G,mod-1-(mod-1)/len);
        for(int i=0;i<n;i+=len) {
            ll t=1;
            for(int j=0;j<mid;j++,t=t*w%mod) {
                ll u=a[i+j],v=a[i+j+mid]*t%mod;
                a[i+j]=(u+v)%mod;
                a[i+j+mid]=(u-v+mod)%mod;
            }
        }
    }
    if(flag==-1) {
        ll inv=ksm(n,mod-2);
        for(int i=0;i<n;i++) a[i]=a[i]*inv%mod;
    }
}

ll f[N];
int n,w[N];
int sum[N];
int binary(int lx,int rx) {
    int mid,l=lx,r=rx;
    while(l<r) {
        mid=l+r+1>>1;
        if(sum[mid]-sum[lx-1]<=sum[rx]-sum[mid]) l=mid;
        else r=mid-1;
    }
    return l;
}

void solve(int l,int r,ll *f) {
    if(l==r) {
        f[0]=1;
        f[w[l]]=mod-1;
        return ;
    }
    int mid=binary(l,r);
    const int d=ceil(log2(sum[r]-sum[l-1]))+1;
    ll *a=new ll[(1<<d)+5],*b=new ll[(1<<d)+5];
    for(int i=0;i<(1<<d);i++) a[i]=b[i]=0;
    solve(l,mid,a),solve(mid+1,r,b);
    NTT(a,d,1),NTT(b,d,1);
    for(int i=0;i<(1<<d);i++) a[i]=a[i]*b[i]%mod;
    NTT(a,d,-1);
    for(int i=0;i<(1<<d);i++) f[i]=a[i];
    delete a;
    delete b;
}

ll ans;
int main() {
    n=Get();
    for(int i=1;i<=n;i++) w[i]=Get();
    sort(w+2,w+1+n);
    for(int i=2;i<=n;i++) sum[i]=sum[i-1]+w[i];
    solve(2,n,f);
    int tot=sum[n]-sum[1];
    for(int i=0;i<=tot;i++) {
        (ans+=f[i]*w[1]%mod*ksm(w[1]+i,mod-2)%mod)%=mod;
    }
    cout<<ans;
    return 0;
}

转载于:https://www.cnblogs.com/hchhch233/p/10159821.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值