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\n",(f[Lg[X]]+Mod)%Mod);
  return 0;
}

  

posted @ 2017-07-12 21:28  Fenghr  阅读(224)  评论(1编辑  收藏  举报