[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<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
首先对于模数m的原根G,每个i=1~m-1,都有唯一的j属于[0,m-2],使得$G^{j}=i$
求原根可以先找出p-1的所有质因数,然后从2开始枚举,如果对任意的p-1的质因数p[i],x的(p-1)/p[i]次幂模p都不等于1,则x是p的一个原根
原根一般比较小,可以暴力找
那么乘法就转化为加法,以下所说的数的都是离散对数,也就是每个j对应的i
对于x,如果i+j=x
那么x的方案数f[x]显然要加上f[i]*f[j]
这显然是一个多项式形式,也就是多项式乘法
因为求的是小于m-1的方案数,所以每次相乘要把大于m-1的方案合并
原题相当于n个多项式相乘,求某一项的系数
因为n很大,而且因为合并答案,所以长度不会超过2*m-2
用快速幂套NTT就可以了
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 typedef long long lol; 8 lol MOD=1004535809; 9 lol X,S,G,p[10001],a[80001],b[80001],R[80001]; 10 int pri[100001]; 11 lol qpow(lol x,lol y,lol Mod) 12 { 13 lol res=1; 14 while (y) 15 { 16 if (y&1) res=res*x%Mod; 17 x=x*x%Mod; 18 y>>=1; 19 } 20 return res; 21 } 22 lol getG(lol m) 23 { 24 lol i,j; 25 lol x=m-1,tot=0,flag; 26 for (i=2;i<=sqrt(m-1);i++) 27 if (x%i==0) 28 { 29 pri[++tot]=i; 30 while (x%i==0) x/=i; 31 } 32 if (x!=1) 33 { 34 pri[++tot]=x; 35 } 36 for (i=2;;i++) 37 { 38 flag=1; 39 for (j=1;j<=tot;j++) 40 if (qpow(i,(m-1)/pri[j],m)==1) 41 { 42 flag=0; 43 break; 44 } 45 if (flag) 46 return i; 47 } 48 } 49 void NTT(lol *A,int len,int o) 50 {lol i,j,k; 51 for (i=0;i<len;i++) 52 if (i<R[i]) swap(A[i],A[R[i]]); 53 for (i=1;i<len;i<<=1) 54 { 55 lol wn=qpow(3,(MOD-1)/(i<<1),MOD),x,y; 56 if (o==-1) wn=qpow(wn,MOD-2,MOD); 57 for (j=0;j<len;j+=(i<<1)) 58 { 59 lol w=1; 60 for (k=0;k<i;k++,w=w*wn%MOD) 61 { 62 x=A[j+k];y=w*A[j+k+i]%MOD; 63 A[j+k]=(x+y)%MOD; 64 A[j+k+i]=(x-y+MOD)%MOD; 65 } 66 } 67 } 68 if (o==-1) 69 { 70 lol tmp=qpow(len,MOD-2,MOD); 71 for (i=0;i<len;i++) 72 A[i]=A[i]*tmp%MOD; 73 } 74 } 75 int main() 76 {lol i,x,len,lg=0,n,m; 77 cin>>n>>m>>X>>S; 78 G=getG(m); 79 lol tmp=1; 80 for (i=0;i<m-1;i++) 81 { 82 p[tmp]=i; 83 tmp=(tmp*G)%m; 84 } 85 for (i=1;i<=S;i++) 86 { 87 scanf("%lld",&x); 88 if (x) a[p[x]]++; 89 } 90 len=1; 91 while (len<=2*m-2) len*=2,lg++; 92 for (i=0;i<len;i++) 93 R[i]=(R[i>>1]>>1)|((i&1)<<(lg-1)); 94 b[0]=1; 95 while (n) 96 { 97 if (n&1) 98 { 99 NTT(a,len,1); 100 NTT(b,len,1); 101 for (i=0;i<len;i++) 102 b[i]=b[i]*a[i]%MOD; 103 NTT(b,len,-1); 104 NTT(a,len,-1); 105 for (i=m-1;i<len;i++) 106 b[i%(m-1)]+=b[i],b[i%(m-1)]%=MOD,b[i]=0; 107 } 108 NTT(a,len,1); 109 for (i=0;i<len;i++) 110 a[i]=a[i]*a[i]%MOD; 111 NTT(a,len,-1); 112 for (i=m-1;i<len;i++) 113 a[i%(m-1)]+=a[i],a[i%(m-1)]%=MOD,a[i]=0; 114 n>>=1; 115 } 116 printf("%lld\n",b[p[X]]); 117 }