小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
对于全部的数据,1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复。
原根,离散对数
设朴素状态
Fi,j−>Fi+1,jxmodm
F
i
,
j
−
>
F
i
+
1
,
j
x
mod
m
乘法不会搞,考虑取离散对数:
原根的定义:
模数p的原根g满足其模p意义下的阶为phi(p)
换句话说,其满足:
在模意义下
对于任意在[1,p-1]区间中的互异i,j,都有
gi≠gj
g
i
≠
g
j
,显然等价于
gi=1
g
i
=
1
当且仅当i=p-1.
也就是说
g1,g2,g3...gp−1
g
1
,
g
2
,
g
3
.
.
.
g
p
−
1
与
1,2,3...m−1
1
,
2
,
3...
m
−
1
形成了一一映射。
若
gx=r
g
x
=
r
,则定义r的离散对数:
ind(r)=x
i
n
d
(
r
)
=
x
显然a*b等价于
gind(a)+ind(b)
g
i
n
d
(
a
)
+
i
n
d
(
b
)
这样就完成了转乘法为加法的壮举。
然后对朴素状态优化就行。其实就是对一个多项式求幂,NTT加速。
有一个坑点:输入可能有0,0没有离散对数。
如何求原根:
对于奇素数p有一个较快求法,若对于p-1的任意质因子px,都有
gipx≠1
g
i
p
x
≠
1
,则i是p的原根。
证明:
不漏:根据费马小定理
gp−1=1
g
p
−
1
=
1
,因此若存在=1的就重复了,必然不是原根。
不多:即为要验证是否
gi=1
g
i
=
1
当且仅当i=p-1.
假设存在一个
gi=1
g
i
=
1
,且i!=p-1。由费马小定理,i是p-1的约数。
也就是说他比p-1缺少了某些因数。这样每次去掉一个质因子,这样的i必然会被某一次的i/px包含。
包含时,有
gipx=1
g
i
p
x
=
1
。因此只需要验证是否
若对于p-1的任意质因子px,都有 gipx≠1 g i p x ≠ 1 ,则i是p的原根。
验证一个数的复杂度是质因子也就是log级别的(实际更小),由于原根一般不大,所以从1开始枚举检验即可。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int mo=1004535809,G=3,M0=8010*2*2;
int n,m,x,S,g;
int fc[1000];
int lo[10000];
ll C[M0];
int ksm(ll x,int y,int mo) {
ll ret=1;
for (; y!=0; y>>=1) {
if (y&1) ret=ret*x%mo;
x=(ll)x*x%mo;
}
return ret;
}
int getroot(int f) {
int w=f-1;
for (int i=2; w!=1; i++) if (w%i==0) {
fc[++fc[0]]=i;
while (w%i==0) w/=i;
}
for (int i=1; i<f; i++) {
int flag=1;
for (int j=1; j<=fc[0]; j++) if (ksm(i,(f-1)/fc[j],f)==1) {
flag=0; break;
}
if (flag) return i;
}
}
ll D[M0],tA[M0];
ll h[M0],M,eps[M0],ieps[M0];
void dft(ll *A,int sig) {
for (int i=0; i<M; i++) if (h[i]<i) swap(A[h[i]],A[i]);
for (int m=2; m<=M; m*=2) {
int hf=m/2,zg=(sig==-1)?ieps[m]:eps[m];
for (ll i=0,z=1; i<hf; i++,z=z*zg%mo)
for (int j=i; j<M; j+=m) {
int u=A[j],v=A[j+hf]*z%mo;
A[j]=(u+v)%mo,A[j+hf]=(u-v+mo)%mo;
}
}
if (sig==-1) {
int iv=ksm(M,mo-2,mo);
for (int i=0; i<M; i++) A[i]=A[i]*iv%mo;
}
}
void fft(ll *A,ll *B) {
dft(A,1);
for (int i=0; i<M; i++) A[i]=A[i]*B[i]%mo;
dft(A,-1);
for (int i=m; i<M; i++)
A[i-(m-1)]=(A[i-(m-1)]+A[i])%mo,A[i]=0;
}
void vec_ksm(ll *A,int y) {
D[0]=1;
for (int z=1,len=0; y!=0; y>>=1) {
memcpy(tA,A,M*sizeof A[0]);
dft(tA,1);
if (y&1) fft(D,tA);
fft(A,tA);
}
memcpy(A,D,sizeof D);
}
int main() {
freopen("4051.in","r",stdin);
cin>>n>>m>>x>>S;
g=getroot(m);
for (int z=1,hh=g; z<m; z++,hh=hh*g%m) lo[hh]=z;
for (int i=1; i<=S; i++) {
int t; scanf("%d",&t); C[lo[t%m]]++;
}
C[0]=0;
for (M=1; M<m*2; M*=2);
for (int i=1; i<M; i++) h[i]=(h[i>>1]>>1) + (M>>1) * (i&1);
for (int i=1; i<=M; i*=2) eps[i]=ksm(G,(mo-1)/i,mo),ieps[i]=ksm(eps[i],mo-2,mo);
vec_ksm(C,n);
cout<<C[lo[x]]<<endl;
}