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; }