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 21 2
Sample Output
8【样例说明】可以生成的满足要求的不同的数列有(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)。
Data Constraint
对于10%的数据,1<=N<=1000;
对于30%的数据,3<=M<=100;
对于60%的数据,3<=M<=800;
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复。
解法:
首先可以想到一个暴力dp,设f[i][j]表示选到第i个数,mod m=j的方案数。那么显然转移方程为f[i+1][j*k%m]+=f[i][j];
即f[i+1][j*k%m]=∑f[i][j]*num[k](序列S中mod m=k的数的个数)
因为m是质数,所以m存在原根,我们可以通过离散对数的变换将乘法变为加法
原式变为f[i+1][(ind[j]+ind[k])%(m-1)]=∑f[i][ind[j]]*num[ind[k]]
为卷积形式,可使用FFT优化。
N很大,所以套一个快速幂即可
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long ll; int n,m,x,L,i,k,N,ws,NN; int a[100011],pos[100011],b[100011],B[100011],d[100011]; int w[100011],ind[100011],h[100011]; int mo=1004535809; int mi(int x,int z) { int l; l=1; while(z){ if(z%2==1)l=(ll)l*x%mo; z/=2; x=(ll)x*x%mo; } return l; } bool isroot(int x) { int i,l; l=1; for(i=1;i<m-1;i++){ l=l*x%m; if(l==1)return false; } return true; } void prepare() { int i,j,k,g,l; //离散对数 for(i=1;i<m;i++)if(isroot(i)){ g=i; break; } l=1; for(i=1;i<m;i++){ l=l*g%m; ind[l]=i; } //fftN次单位复数根 N=1; while(N<2*m){ N*=2; ws++; } w[0]=1; w[1]=mi(3,(mo-1)/N); for(i=2;i<=N;i++)w[i]=(ll)w[i-1]*w[1]%mo; for(i=0;i<N;i++){ for(j=0;j<ws;j++){ k=(i&(1<<(ws-1-j))); if(k)pos[i]+=(1<<j); } } NN=mi(N,mo-2); } void Dft(int *a,int sig) { int i,half,u,v,j,l,wi; for(i=0;i<N;i++)h[pos[i]]=a[i]; for(l=1;l<=ws;l++){ half=1<<(l-1); for(i=0;i<half;i++){ wi=(sig>0)?w[i<<(ws-l)]:w[N-(i<<(ws-l))]; for(j=i;j<N;j+=(1<<l)){ u=h[j];v=(ll)h[j+half]*wi%mo; h[j]=(u+v)%mo; h[j+half]=((u-v)%mo+mo)%mo; } } } for(i=0;i<N;i++)a[i]=h[i]; } void fft(int *a,int *b) { int i; Dft(a,1); Dft(b,1); for(i=0;i<N;i++)a[i]=(ll)a[i]*b[i]%mo; Dft(a,-1); for(i=0;i<N;i++)a[i]=(ll)a[i]*NN%mo; for(i=m;i<N;i++){ a[(i%(m-1)==0)?(m-1):(i%(m-1))]=(a[(i%(m-1)==0)?(m-1):(i%(m-1))]+a[i])%mo; a[i]=0; } } void MT(int *b,int z) { int i; d[0]=1; while(z){ if(z%2==1){ for(i=0;i<N;i++)B[i]=b[i]; fft(d,b); for(i=0;i<N;i++)b[i]=B[i]; } z/=2; for(i=0;i<N;i++)B[i]=b[i]; fft(b,B); } } void Work() { a[ind[1]]++; MT(b,n); fft(a,d); printf("%d\n",a[ind[x]]); } int main() { scanf("%d%d%d%d",&n,&m,&x,&L); prepare(); for(i=1;i<=L;i++){ scanf("%d",&k); k%=m; b[ind[k]]++; } b[0]=0; Work(); }
时间: 2024-10-10 21:17:52