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中的所有元素。

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<=10^9,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复
 
好厉害。。。
首先有10分算法,设f[i][j]表示选i个数,乘积模M结果为x的方案数。
然后因为M为质数,我们可以求出M的原根g,这样转移就可以写成关于g的生成函数,就可以用NTT来加速了。
接下来发现每次乘的都是相同的多项式,那么多项式快速幂即可。
#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
const int BufferSize=1<<16;
char buffer[BufferSize],*head,*tail;
inline char Getchar() {
    if(head==tail) {
        int l=fread(buffer,1,BufferSize,stdin);
        tail=(head=buffer)+l;
    }
    return *head++;
}
inline int read() {
    int 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 int maxn=18010;
const int p=1004535809;
const int G=3;
const int NUM=15;
typedef long long ll;
ll wn[maxn],inv;
ll pow(ll n,ll m,ll mod=p) {
    ll ans=1;
    for(;m;m>>=1,(n*=n)%=mod) if(m&1) (ans*=n)%=mod;
    return ans;
}
void NTT(ll* A,int len,int tp) {
    int j=len>>1,c=0;
    rep(i,1,len-2) {
        if(i<j) swap(A[i],A[j]);int k=len>>1;
        while(j>=k) j-=k,k>>=1;j+=k;
    }
    for(int i=2;i<=len;i<<=1) {
        c++;
        for(int j=0;j<len;j+=i) {
            ll w=1;
            for(int k=j;k<j+(i>>1);k++) {
                ll u=A[k],t=w*A[k+(i>>1)]%p;
                A[k]=(u+t)%p;A[k+(i>>1)]=(u-t+p)%p;
                (w*=wn[c])%=p;
            }
        }
    }
    if(tp<0) {
        rep(i,1,len/2-1) swap(A[i],A[len-i]);
        ll inv=pow(len,p-2);
        rep(i,0,len-1) (A[i]*=inv)%=p; 
    }
}
int check(int g,int m) {
    for(int i=2;i*i<m;i++) if((m-1)%i==0&&(pow(g,i,m)==1||pow(g,(m-1)/i,m)==1)) return 0;
    return 1;
}
int n,m,X,S,len,gs=2,c[maxn];
ll D[maxn];
void mul(ll* A,ll* B) {
    rep(i,0,len-1) D[i]=B[i];
    NTT(A,len,1);NTT(D,len,1);
    rep(i,0,len-1) (A[i]*=D[i])%=p;
    NTT(A,len,-1);
    dwn(i,len-1,m-1) (A[i-m+1]+=A[i])%=p,A[i]=0;
}
ll A[maxn],B[maxn];
int main() {
    rep(i,0,NUM-1) wn[i]=pow(G,(p-1)/(1<<i));
    n=read();m=read();X=read();S=read();
    len=1;while(len<=(m<<1)) len<<=1;
    while(!check(gs,m)) gs++;
    int m0=1;c[1]=0;
    rep(i,1,m-2) (m0*=gs)%=m,c[m0]=i;
    rep(i,1,S) {
        int x=read()%m;
        if(x) A[c[x]]=1;
    }
    B[0]=1;
    for(;n;mul(A,A),n>>=1) if(n&1) mul(B,A);
    printf("%lld\n",B[c[X]]);
    return 0;
}

  

posted @ 2016-03-24 17:40  wzj_is_a_juruo  阅读(201)  评论(0编辑  收藏  举报