zoukankan      html  css  js  c++  java
  • [PKUWC2018]猎人杀 解题报告

    题目

    (n) 个猎人,第 (i) 个猎人有一个仇恨度 (w_i) ,被打中的概率是 (frac{w_i}{sum})(sum) 是活着的猎人的仇恨度总和。

    一开始由第一个猎人开出一枪,问第一个猎人是最后死的概率。

    (nle 10^5 , sumle 10^5)

    其实我以前在搞约翰夫抽杀时想过和这个一模一样的题,当时感觉十分不可做,现在看来是我太菜了,没想到真的有这道题。

    每次打中一个猎人总的仇恨度会发生变化,感觉十分不可做,所以要考虑转化一下问题。

    假设总的仇恨度为 (s_0) ,目前活着的人的仇恨度总和为 (s_1) ,则第 (i) 个人被打中的概率为 (p_i=frac{w_i}{s_1}) ,为了更好的做题,考虑把分母转化为 (s_0) ,那么有 (p_i=frac{w_i+(s_0-s_1)p_i}{s_0}) ,再把 (p_i=frac{w_i}{s_1}) 代到前面变成 (frac{w_i}{s_1}=frac{w_i+(s_0-s_1)p_i}{s_0}) ,化简可得 (p_i=frac{w_i}{s_0})

    所以题目可以转化成每个人被打中的概率是 (frac{w_i}{s_0}) ,可以打中死人,问第一个猎人是最后死的概率。

    这种反直觉的概率果然不是给我这种粗人玩的Orz

    但是最后死的概率并不好求,考虑运用套路,通过容斥将限制放宽,假设 (S) 是一个以猎人为元素的集合, (F(x)) 是第一个猎人死后剩下猎人集合包含 (S) 的概率,那么有 (Ans=sumlimits (-1)^{|S|} F(S))

    考虑枚举第一个猎人是第几个死的,可以得到 (F(S)=sumlimits_{i=0}^{infty} (frac{s_0-w_1-sum(S)}{s_0})^ifrac{w_1}{s_0}) ,通过等比数列求和公式和对极限的运用,能化简成 (F(S)=frac{w_1}{w_1+sum(S)}) ,所以有 (Ans=sumlimits (-1)^{|S|} frac{w_1}{w_1+sum(S)})

    直接枚举集合再考虑优化不太可能,所以要另寻蹊径,发现数据范围中有 (s_0le 10^5) 这一条,而且 (F(S)) 也只有 (sum(S)) 这一变量,考虑枚举 (sum(S)) 来求答案。

    假设 (G(x)=sumlimits_{sum(S)=x} (-1)^{|S|}) ,那么有 (Ans=sumlimits_{i=1}^{s_0} frac{w_1G(i)}{w_1+i})

    这个 (G(x)) 的限制条件显然可以用生成函数来求解,由于每个猎人最多选一个,所以对于第 (i) 个猎人,他的生成函数就只有第 (0) 项是 (1) 和第 (w_i) 项是 (-1) ,将所有的生成函数乘起来的第 (x) 项正是 (G(x))

    这个东西可以用堆+NTT或者分治+NTT做到 (mathcal{O(nlog^2 n)})

    #include<bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int M=2e5+5,JYY=998244353;
    
    int n,Sum=0,Ans=0,w[M];
    int read(){
        int x=0,y=1;char ch=getchar();
        while(ch<'0'||ch>'9') y=(ch=='-'?-1:1),ch=getchar();
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return x*y;
    }
    
    int qpow(int x,int y){
        int res=1;x%=JYY,y%=(JYY-1);
        for(;y;x=x*x%JYY,y>>=1) if(y&1) res=res*x%JYY;
        return res;
    }
    
    namespace Poly{
        int rev[M],wn[35];
        void getwn(){ for(int i=0;i<=25;i++) wn[i]=qpow(3,(JYY-1)/(1<<i));}
        void NTT(int f[],int L,int inv){
            for(int i=0;i<L;i++) if(i<rev[i]) swap(f[i],f[rev[i]]);
            for(int mid=1,id=1;mid<L;mid<<=1,id++)
                for(int R=(mid<<1),j=0;j<L;j+=R)
                    for(int k=0,w=1;k<mid;k++,w=w*wn[id]%JYY){
                        int u=f[j+k],v=w*f[j+k+mid]%JYY;
                        f[j+k]=(u+v)%JYY;
                        f[j+k+mid]=(u-v+JYY)%JYY;
                    }
            if(inv==-1){
                reverse(f+1,f+L);int ny=qpow(L,JYY-2);
                for(int i=0;i<L;i++) f[i]=f[i]*ny%JYY;
            }
        }
        void poly_mul(int f[],int g[],int h[],int L1,int L2){
            int len=1,L=0;static int a[M],b[M];
            for(;len<L1+L2;len<<=1) L++;
            for(int i=0;i<len;i++){
                rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));
                a[i]=(i<L1?f[i]:0),b[i]=(i<L2?g[i]:0);
            }
            NTT(a,len,1),NTT(b,len,1);
            for(int i=0;i<len;i++) h[i]=a[i]*b[i];
            NTT(h,len,-1);
            for(int i=L1+L2-1;i<len;i++) h[i]=0;
        }
    }using namespace Poly;
    
    vector<int> cun[M];
    void work(int u,int l,int r){
        if(l==r){
            cun[u].push_back(1);
            for(int i=1;i<w[l];i++) cun[u].push_back(0);
            cun[u].push_back(JYY-1);
            return ;
        }
        static int f[M],g[M],h[M];
        int mid=(l+r)>>1;
        work(u<<1,l,mid),work(u<<1|1,mid+1,r);
        int szl=cun[u<<1].size(),szr=cun[u<<1|1].size();
        for(int i=0;i<szl;i++) f[i]=cun[u<<1][i];
        for(int i=0;i<szr;i++) g[i]=cun[u<<1|1][i];
        poly_mul(f,g,h,szl,szr);
        for(int i=0;i<szl+szr-1;i++) cun[u].push_back(h[i]);
    }
    
    void solve(){
        n=read();
        for(int i=1;i<=n;i++) Sum+=w[i]=read();
        getwn();work(1,2,n);
        for(int i=0;i<=Sum-w[1];i++) Ans=(Ans+cun[1][i]*w[1]%JYY*qpow(w[1]+i,JYY-2)%JYY)%JYY;
        printf("%lld
    ",Ans);
    }
    
    signed main(){
        // freopen("shuju.in","r",stdin);
        solve();
    }
    
  • 相关阅读:
    如何使用谷歌的网页删除请求工具?
    已有记录表添加特定排序主键ID
    用ASP实现超长内容的word模板替换objDoc.Content.Find.Execute
    内网SMTP发送失败的曲线救国之策
    IIS无法在后台生成WORD文件的故障
    WINDOWS2003进行WindowsFTP设置的一些注意事项
    解决IISASP调用XmlHTTP出现msxml3.dll (0x80070005) 拒绝访问的错误
    [转]Cate:我是如何准备Google面试的
    Ubuntu的启动配置文件grub.cfg(menu.lst)设置指南
    Linux启动过程详解
  • 原文地址:https://www.cnblogs.com/wzp-blog/p/14238497.html
Copyright © 2011-2022 走看看