[BZOJ3992] [SDOI2015]序列统计
[BZOJ3992] [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中的所有元素。1<=N<=10^9,3<=M<=8000,M为质数0<=x<=M-1,输入数据保证集合S中元素不重复x∈[1,m-1]
集合中的数∈[0,m-1]
Output
一行,一个整数,表示你求出的种类数mod 1004535809的值。
Sample Input
4 3 1 2
1 2
Sample Output
8
【样例说明】可以生成的满足要求的不同的数列有(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)
试题分析
原根的一类性质:设\(p\)为\(mod\)的原根,那么\(p^0\to p^{mod-2} \pmod{mod}\)可以表示出1~mod-1的所有数。
这样我们就可以对于每个数求指标(\(p^x=y\),其中\(x\)为\(y\)的指标),然后我们就可以把乘法变成指数上的加法,NTT多项式快速幂即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define G 3
#define Mod 1004535809LL
inline LL read(){
LL x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const LL MAXN = 100010;
const LL INF = 2147473600;
LL N,M,X,S; LL top,rev[MAXN+1];
LL s[MAXN+1],sta[MAXN+1];
LL a[MAXN+1];
inline LL Pow(LL A,LL B,LL P){
LL res=1;
while(B){
if(B&1) res=res*A%P;
A=A*A%P; B>>=1;
} return res;
}
inline LL GetN(LL x){
LL y=x-1;
for(LL i=2;y!=1;i++) {
if(y%i==0) sta[++top]=i;
while(y%i==0) y/=i;
}
for(LL i=2;;i++){
bool flag=1;
for(LL j=1;j<=top;j++)
if(Pow(i,(x-1)/sta[j],x)==1) {flag=false; break;}
if(flag) return i;
}
}
LL a1[MAXN+1],b1[MAXN+1],c1[MAXN+1],b[MAXN+1],t[MAXN+1];
inline void NTT(LL *A,LL lim,LL type){
for(LL i=0;i<lim;i++) if(rev[i]>i) swap(A[rev[i]],A[i]);
for(LL mid=1;mid<lim;mid<<=1){
LL Wn = Pow(G , type==1?(Mod-1)/(mid<<1):(Mod-1-(Mod-1)/(mid<<1)) , Mod)%Mod;//-
for(LL R=mid<<1,j=0;j<lim;j+=R){
LL w = 1;
for(LL k=0;k<mid;k++){
LL x=A[j+k] , y=w*A[j+k+mid]%Mod; //-
A[j+k]=(x+y)%Mod; A[j+k+mid]=(x-y%Mod+Mod)%Mod;
w=w*Wn%Mod;
}
}
} if(type==1) return ; //reverse(A+1,A+lim);
LL INV=Pow(lim,Mod-2,Mod)%Mod;
for(LL i=0;i<lim;i++) A[i]=A[i]*INV%Mod; return ;
}
LL lim=1;
inline void Mul(LL *A,LL *B,LL *C){
for(LL i=0;i<M;i++) a1[i]=B[i],b1[i]=C[i];
for(LL i=M;i<lim;i++) a1[i]=b1[i]=0;
NTT(a1,lim,1); NTT(b1,lim,1);
for(LL i=0;i<lim;i++) A[i]=a1[i]*b1[i]%Mod;
NTT(A,lim,-1);
for(LL i=lim-1;i>=M-1;i--){
A[i%(M-1)]=(A[i%(M-1)]+A[i])%Mod; A[i]=0;
} return ;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read(),M=read(),X=read(),S=read(); LL Now=1LL,T=GetN(M);
for(LL i=0;i<M-1;i++) t[Now]=i,Now=Now*T%M;//cout<<Now<<" "<<T<<endl;
for(LL i=1;i<=S;i++) {LL x=read()%Mod; if(x) a[t[x]]++; } lim=1; int l=0;
while(lim<=2*(M-1)) lim<<=1,++l;
for(LL i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
memset(b,0,sizeof(b)); b[0]=1;
while(N){
if(N&1) Mul(b,a,b);
Mul(a,a,a); N>>=1;
} printf("%lld\n",b[t[X]]%Mod);
return 0;
}
你——悟到了么?