[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

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 }

 

posted @ 2018-02-05 09:23  Z-Y-Y-S  阅读(313)  评论(0编辑  收藏  举报