BZOJ3992: [SDOI2015]序列统计
Description
小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
Input
一行,四个整数,N、M、x、|S|,其中|S|为集合S中元素个数。第二行,|S|个整数,表示集合S中的所有元素。
Output
一行,一个整数,表示你求出的种类数mod 1004535809的值。
Sample Input
4 3 1 2
1 2
1 2
Sample Output
8
HINT
【样例说明】
可以生成的满足要求的不同的数列有(1,1,1,1)、(1,1,2,2)、(1,2,1,2)、(1,2,2,1)、(2,1,1,2)、(2,1,2,1)、(2,2,1,1)、(2,2,2,2)。
【数据规模和约定】
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=10^9,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
好厉害。。。
首先有10分算法,设f[i][j]表示选i个数,乘积模M结果为x的方案数。
然后因为M为质数,我们可以求出M的原根g,这样转移就可以写成关于g的生成函数,就可以用NTT来加速了。
接下来发现每次乘的都是相同的多项式,那么多项式快速幂即可。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=Getchar(); for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1; for(;isdigit(c);c=Getchar()) x=x*10+c-'0'; return x*f; } const int maxn=18010; const int p=1004535809; const int G=3; const int NUM=15; typedef long long ll; ll wn[maxn],inv; ll pow(ll n,ll m,ll mod=p) { ll ans=1; for(;m;m>>=1,(n*=n)%=mod) if(m&1) (ans*=n)%=mod; return ans; } void NTT(ll* A,int len,int tp) { int j=len>>1,c=0; rep(i,1,len-2) { if(i<j) swap(A[i],A[j]);int k=len>>1; while(j>=k) j-=k,k>>=1;j+=k; } for(int i=2;i<=len;i<<=1) { c++; for(int j=0;j<len;j+=i) { ll w=1; for(int k=j;k<j+(i>>1);k++) { ll u=A[k],t=w*A[k+(i>>1)]%p; A[k]=(u+t)%p;A[k+(i>>1)]=(u-t+p)%p; (w*=wn[c])%=p; } } } if(tp<0) { rep(i,1,len/2-1) swap(A[i],A[len-i]); ll inv=pow(len,p-2); rep(i,0,len-1) (A[i]*=inv)%=p; } } int check(int g,int m) { for(int i=2;i*i<m;i++) if((m-1)%i==0&&(pow(g,i,m)==1||pow(g,(m-1)/i,m)==1)) return 0; return 1; } int n,m,X,S,len,gs=2,c[maxn]; ll D[maxn]; void mul(ll* A,ll* B) { rep(i,0,len-1) D[i]=B[i]; NTT(A,len,1);NTT(D,len,1); rep(i,0,len-1) (A[i]*=D[i])%=p; NTT(A,len,-1); dwn(i,len-1,m-1) (A[i-m+1]+=A[i])%=p,A[i]=0; } ll A[maxn],B[maxn]; int main() { rep(i,0,NUM-1) wn[i]=pow(G,(p-1)/(1<<i)); n=read();m=read();X=read();S=read(); len=1;while(len<=(m<<1)) len<<=1; while(!check(gs,m)) gs++; int m0=1;c[1]=0; rep(i,1,m-2) (m0*=gs)%=m,c[m0]=i; rep(i,1,S) { int x=read()%m; if(x) A[c[x]]=1; } B[0]=1; for(;n;mul(A,A),n>>=1) if(n&1) mul(B,A); printf("%lld\n",B[c[X]]); return 0; }