您的位置:首页 > 其它

bzoj3992: [SDOI2015]序列统计

2016-12-11 16:10 253 查看

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的值。

将S中元素和x取以m的某个原根为底的离散对数之后转化为生成函数的n次幂在%(m-1)=x的位置的系数和,可以用快速幂套ntt计算

#include<cstdio>
#include<algorithm>
typedef long long i64;
const int P=1004535809,g=3;
int n,m,x,s,v0[8007],v1[8007],N=2,K=0,r[17777];
int rp[8007],rw[8007];
i64 pow(i64 a,int n){
i64 v=1;
for(;n;a=a*a%P,n>>=1)if(n&1)v=v*a%P;
return v;
}
void ntt(i64*a,int t){
static i64 e[17777];
for(int i=0;i<N;++i)if(i>r[i])std::swap(a[i],a[r[i]]);
for(int i=1;i<N;i<<=1){
i64 w=pow(g,(t*(P-1)/(i<<1)+P-1)%(P-1));
e[0]=1;
for(int j=1;j<i;++j)e[j]=e[j-1]*w%P;
for(int j=0;j<N;j+=i<<1){
for(int k=0;k<i;++k){
i64 x=a[j+k],y=a[j+k+i]*e[k]%P;
a[j+k]=(x+y)%P;
a[j+k+i]=(x-y)%P;
}
}
}
if(t<0){
i64 I=pow(N,P-2);
for(int i=0;i<N;++i)a[i]=a[i]*I%P;
}
}
void mul(int*a,int*b){
static i64 A[17777],B[17777];
for(int i=0;i<m;++i)A[i]=a[i];
for(int i=m;i<N;++i)A[i]=0;
ntt(A,1);
if(a!=b){
for(int i=0;i<m;++i)B[i]=b[i];
for(int i=m;i<N;++i)B[i]=0;
ntt(B,1);
for(int i=0;i<N;++i)A[i]=A[i]*B[i]%P;
}else for(int i=0;i<N;++i)A[i]=A[i]*A[i]%P;
ntt(A,-1);
for(int i=0;i<m-1;++i)a[i]=(A[i]+A[i+m-1])%P;
}
int root(int n){
if(n==2)return 1;
for(int i=2;i<n;++i){
for(int j=1,c=1;j<n-1;++j){
c=c*i%n;
if(c==1)goto o;
}
return i;
o:;
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&x,&s);
rp[0]=1;
rp[1]=root(m);
for(int i=2;i<m-1;++i)rp[i]=rp[i-1]*rp[1]%m;
for(int i=0;i<m-1;++i)rw[rp[i]]=i;
for(int i=0,a;i<s;++i){
scanf("%d",&a);
a%=m;
if(a)++v0[rw[a]];
}
for(;N<=m*2+3;N<<=1,++K);
for(int i=1;i<N;++i)r[i]=r[i>>1]>>1|(i&1)<<K;
v1[0]=1;
for(;n;mul(v0,v0),n>>=1)if(n&1)mul(v1,v0);
printf("%d",(v1[rw[x]]+P)%P);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: