3992: [SDOI2015]序列统计
Time Limit: 30 Sec Memory Limit: 128 MB
Submit: 549 Solved: 267
[Submit][Status][Discuss]
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中元素不重复
首先对于暴力dp还是比较好想的,f[i][j]表示第i个数乘积为j的个数。这样的复杂度是O(nm^2)的。
然后我们法相取模的数是一个质数,如果我们对于这个数求出原根以后,那么我们就可以吧转移力的乘法变成加法。
我们如果用a[i]表示这一位的模数是i的个数,b[i]表示上一位。那么
这样式子就变成了卷积的形式,因为有取模,所以可以用NTT降到O(nmlogm)
然后再看这个式子,可以发现每次乘的sum[i-j]都是一样的,所以可以把n那一位快速幂一下,这样就变成了O(mlogmlogn)
NTT的做法可以看一下这篇博客 http://blog.csdn.net/acdreamers/article/details/39026505
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define D 1004535809
#define LL long long
const int M=50000;
int n,m,x,s[M],dig[M],rev[M],N,L,g,pos[M];
LL a[M],b[M],c[M],d[M],mi[M],ni,sum[M],ans[M];
inline LL quickpow(LL x,LL y,LL z){
LL Ans=1;
while(y){
if(y&1) Ans=Ans*x%z;
y>>=1; x=x*x%z;
}
return Ans;
}
inline bool check(int x){
for(int i=2;i*i<=m;++i)
if((m-1)%i==0&&quickpow((LL)x,(LL)(m-1)/i,m)==1) return false;
return true;
}
inline int get_g(){
if(m==2) return 1;
for(int i=2;;++i)
if(check(i)) return i;
}
inline void NTT(LL *a,int f){
int i,j,k;
LL wn,w,x,y;
for(i=0;i<N;++i) d[i]=a[rev[i]];
for(i=0;i<N;++i) a[i]=d[i];
for(i=2;i<=N;i<<=1){
wn=quickpow(3,f==1?(D-1)/i:D-1-(D-1)/i,D);
for(j=0;j<N;j+=i){
w=1;
for(k=j;k<j+i/2;++k){
x=a[k]%D;
y=w*a[k+i/2]%D;
a[k]=(x+y)%D;
a[k+i/2]=(x-y+D)%D;
w=w*wn%D;
}
}
}
if(f==-1) for(i=0;i<N;++i) a[i]=a[i]*ni%D;
}
inline void mul(LL *a,LL *bb,LL *cc){
int i;
for(i=0;i<N;++i) b[i]=bb[i],c[i]=cc[i];
NTT(b,1);NTT(c,1);
for(i=0;i<N;++i) a[i]=b[i]*c[i]%D;
NTT(a,-1);
for(i=m-1;i<N;++i){
a[i-m+1]=(a[i-m+1]+a[i])%D;
a[i]=0;
}
}
inline void calc(LL *a){
ans[0]=1;
while(n){
if(n&1) mul(ans,ans,a);
n>>=1; mul(a,a,a);
}
}
int main(){
int i,j,len;
scanf("%d%d%d%d",&n,&m,&x,&s[0]);
for(N=1;N<m*2;N<<=1,L+=1); N<<=1,L+=1;
for(i=0;i<N;++i){
for(j=i,len=0;j;j>>=1) dig[len++]=j&1;
for(j=0;j<L;++j) rev[i]=(rev[i]<<1)|dig[j];
}
g=get_g();mi[0]=1;
ni=quickpow(N,D-2,D);
for(i=1;i<m-1;++i){
mi[i]=(mi[i-1]*(LL)g)%m;
pos[(int)mi[i]]=i;
}
for(i=1;i<=s[0];++i){
scanf("%d",&s[i]);
if(s[i]==0) continue;
++sum[pos[s[i]]];
}
calc(sum);
printf("%lld\n",ans[pos[x]]);
}