• BZOJ3992 SDOI2015 序列统计


    题面就长这个样子。

    首先我们来分析一下题目里几句"意味深长"的句子。

    1.乘积模M,M为质数,这意味着可能要用到离散对数。

    2.答案对1004535809取模,这个数的原根是3。

    3.M为质数,S内的集合不会重复。

      一般求一些数的乘积的问题都是把它转化成对数来求的。因为M是质数,所以我们通过离散对数可以把问题转化成"N个数加起来模M的值等于X'(X关于M的离散对数值)"。

      然后就可以得到一个很显然的DP,设f[i][j]为第i个数模数为j的方案数,转移就很暴力了鲨。

      发现每一位的数是什么是没有限制的,于是可以搞出它的生成函数,每一项就是S[i]的离散值,用NTT来优化一下多项式的"快速幂",把大于M次的项移一下,最后答案就是X'那一项的系数。

      代码糊上。不到5000ms也不能算丑吧。

    #include    <iostream>
    #include    <cstdio>
    #include    <cstdlib>
    #include    <algorithm>
    #include    <vector>
    #include    <cstring>
    #include    <queue>
    #include    <complex>
    #include    <stack>
    #define LL long long int
    #define ls (x << 1)
    #define rs (x << 1 | 1)
    #define MID LL mid=(l+r)>>1
    using namespace std;
     
    const LL M = 20010;
    const LL Mod = 1004535809;
    LL n,m,X,S,k,G,Inv,L,R[M],Lg[M],a[M],rg[M],f[M];
     
    LL gi()
    {
      LL x=0,res=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
      while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
      return x*res;
    }
     
    inline LL QPow(LL d,LL z,LL mod)
    {
      LL ans=1;
      for(;z;z>>=1,d=d*d%mod)if(z&1)ans=ans*d%mod;
      return ans;
    }
     
    inline int get_rg(int fx)
    {
      int pr[1010],tot=0,Txd=fx-1;
      for(int i=2;i*i<=Txd;++i)
        if(Txd%i==0){
          pr[++tot]=i;
          while(Txd%i==0)Txd/=i;
        }
      if(Txd!=1)pr[++tot]=Txd;
      for(int i=2;i<fx;++i)
        if(__gcd(i,fx)==1){
          int flag=1;
          for(int j=1;j<=tot;++j)
            if(QPow(i,(fx-1)/pr[j],fx)==1){
              flag=0;break;
            }
          if(flag)return i;
        }
      return 0;
    }
     
    inline void NTT(LL *A,LL f)
    {
      for(LL i=0;i<n;++i)if(i>R[i])swap(A[i],A[R[i]]);
      for(LL i=1;i<n;i<<=1){
        LL gn=QPow(3,(f*(Mod-1)/(i<<1)+Mod-1)%(Mod-1),Mod);
        for(LL j=0;j<n;j+=(i<<1)){
          LL g=1,x,y;
          for(LL k=0;k<i;++k,g=1ll*g*gn%Mod){
            x=A[j+k];y=1ll*g*A[i+j+k]%Mod;
            A[j+k]=(x+y)%Mod;A[i+j+k]=(x-y+Mod)%Mod;
          }
        }
      }
      if(f==1)return;LL Inv=QPow(n,Mod-2,Mod);
      for(LL i=0;i<n;++i)A[i]=1ll*A[i]*Inv%Mod;
    }
     
    inline void Mul(LL *A,LL *B)
    {
      LL C[M],D[M];
      for(LL i=0;i<m;++i)C[i]=A[i];for(LL i=m;i<n;++i)C[i]=0;
      NTT(C,1);
      if(A!=B){
        for(LL i=0;i<m;++i)D[i]=B[i];for(LL i=m;i<n;++i)D[i]=0;
        NTT(D,1);
        for(LL i=0;i<n;++i)C[i]=1ll*C[i]*D[i]%Mod;
      }
      else for(LL i=0;i<n;++i)C[i]=1ll*C[i]*C[i]%Mod;
      NTT(C,-1);
      for(LL i=0;i<m-1;++i)A[i]=(1ll*C[i]+C[i+m-1])%Mod;
    }
     
    int main()
    {
      k=gi();m=gi();X=gi();S=gi();
      rg[0]=1,rg[1]=get_rg(m);for(int i=2;i<m-1;++i)rg[i]=rg[i-1]*rg[1]%m;
      for(int i=0;i<m-1;++i)Lg[rg[i]]=i;
      for(LL i=0;i<S;++i){int d=gi();d%=m;if(d)++a[Lg[d]];}
      for(n=1;n<=(2*m+3);n<<=1,++L);
      for(LL i=1;i<n;++i)R[i]=R[i>>1]>>1|(i&1)<<(L-1);
      for(f[0]=1;k;k>>=1,Mul(a,a))if(k&1)Mul(f,a);
      printf("%lld
    ",(f[Lg[X]]+Mod)%Mod);
      return 0;
    }
    

      

  • 相关阅读:
    OleView.exe:查看机器上的COM 组件。
    COM中导出GUID
    进程外组件以及进程间通信方式
    拼接多个 wchar_t *
    wstring to wchar_t*
    BSTR
    GetProcAddress 使用注意事项
    C++和.net的集合类对应
    COM的一些基本概念
    Error Lookup工具
  • 原文地址:https://www.cnblogs.com/fenghaoran/p/7157790.html
Copyright © 2020-2023  润新知