• LOJ 2541 「PKUWC2018」猎人杀——思路+概率+容斥+分治


    题目:https://loj.ac/problem/2541

    看了题解才会……有三点很巧妙。

    1.分母如果变动,就很不好。所以考虑把操作改成 “已经选过的人仍然按 ( w_i ) 的概率被选,但是再次选中一个已经选过的人算作没有操作” 。

    2.然后要容斥,考虑强制点集 S 的人在 1 号点之后被选、其余随意,那么

      ( ans=sumlimits_{S} (-1)^{|S|} sumlimits_{i=0}^{infty} (1-frac{w_1 + w_S}{A})^i frac{w_1}{A} )

      (其中 A 表示 ( sumlimits_{i=1}^{n} w_i ) ,( w_S ) 表示 ( sumlimits_{i in S} w_i ) )

      等比数列求和一下,就是 ( ans=sumlimits_{S} (-1)^{|S|} frac{w_1}{w_1+w_S} )

    3.注意到 ( sum w <=1e5 ) ,所以考虑把那个式子里的分母看做多项式的次数!

      那么算一个 ( prodlimits_{i=2}^{n} (1-x^{w_i}) )(这是考虑选出一个点集 S 的过程)

      然后就是取 ( x^i ) 的系数 ( a_i ) ,给答案贡献 ( frac{a_i}{w_1+i} ) ,最后答案再乘一个 ( w_1 ) 。

    算那个连乘的式子,可以普通地分治。但注意到含有 k 个 w 的乘积式子的次数不是 k ,所以复杂度不是 ( T(n)=T(n/2)+O(nlogn) ) 。(可能是 n2logn?)

    反正把 w 排个序再做就能了。不知道不排序是否也可以。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define vi vector<int>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    const int N=1e5+5,M=(1<<17)+5,mod=998244353;
    int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}
    int pw(int x,int k)
    {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}
    
    int n,w[N],len,r[M],wn[M],wn2[M],inv[M];
    void ntt_pre(int n)
    {
      for(len=1;len<=n;len<<=1);
      for(int R=2;R<=len;R<<=1)
        {
          wn[R]=pw(3,(mod-1)/R);
          wn2[R]=pw(3,(mod-1)-(mod-1)/R);
          inv[R]=pw(R,mod-2);
        }
    }
    void ntt(vi &a,bool fx)
    {
      for(int i=0;i<len;i++)
        if(i<r[i])swap(a[i],a[r[i]]);
      for(int R=2;R<=len;R<<=1)
        {
          int Wn=fx?wn2[R]:wn[R];
          for(int i=0,m=R>>1;i<len;i+=R)
        for(int j=0,w=1;j<m;j++,w=(ll)w*Wn%mod)
          {
            int x=a[i+j],y=(ll)w*a[i+m+j]%mod;
            a[i+j]=upt(x+y); a[i+m+j]=upt(x-y);
          }
        }
      if(!fx)return;  int iv=inv[len];
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*iv%mod;
    }
    vi solve(int L,int R)
    {
      if(L==R)
        {
          vi ret; ret.resize(w[L]+1);
          ret[0]=1; ret[w[L]]=-1;
          return ret;
        }
      int mid=L+R>>1;
      vi a=solve(L,mid), b=solve(mid+1,R);
      int lm=a.size()-1+b.size()-1;
      for(len=1;len<=lm;len<<=1);
      for(int i=0,j=len>>1;i<len;i++)
        r[i]=(r[i>>1]>>1)+((i&1)?j:0);
      a.resize(len); b.resize(len);
      ntt(a,0); ntt(b,0);
      for(int i=0;i<len;i++)a[i]=(ll)a[i]*b[i]%mod;
      ntt(a,1);
      a.resize(lm+1);
      return a;
    }
    int main()
    {
      n=rdn(); int sm=0;
      for(int i=1;i<=n;i++)
        { w[i]=rdn(); if(i>1)sm+=w[i];}//if!!!
      ntt_pre(sm);
      sort(w+2,w+n+1);//
      vi f=solve(2,n); int ans=0;
      for(int i=0;i<=sm;i++)
        if(f[i])ans=(ans+(ll)f[i]*pw(upt(w[1]+i),mod-2))%mod;
      ans=(ll)ans*w[1]%mod;
      printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    Oracle课堂实验一“表的使用”代码。
    Oracle安装时忘记解锁scott用户的解决方案
    PHP中CURL技术模拟登陆抓取网站信息,用与微信公众平台成绩查询
    Fckeditor漏洞利用总结
    js 倒计时 button不可用
    .net 下载图片
    截取字符串
    js 后台弹窗
    账号注册,密码安全级别提示(弱、中、强)代码
    jquery 评论等级(很差,差,一般,好,很好)代码
  • 原文地址:https://www.cnblogs.com/Narh/p/10886569.html
Copyright © 2020-2023  润新知