[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;
}
posted @ 2018-09-01 16:38  wxjor  阅读(197)  评论(0编辑  收藏  举报