bzoj3992

先暴力推了一波dp式子,看上去有点像卷积,可是第i项是和i/j项有关的。

由于m是质数,所以可以尝试用原根,因为原根gi(0~p-2)在模p意义下两两不同并且不为0,所以对一个数x,若x=gi mod (m - 1),那么就可以用i代替x,这样就可以把问题转换为在一个数集中选取n个数它们的和在模(p-1)意义下的值是x,这样dp中第i项就是和第i-j项有关的。

原根的求法,对于一个质数p,对p-1分解质因数p=p1q1*p2q2...pnqn,然后我们从2开始枚举,如果i((p-1)/pi)均不为1,那么i就是p的一个原根,因为原根不大所以这样的方法是没有问题的。

但是有一点要注意,这题中输入的数可能为0,这时候应该直接无视掉,原根无法处理这样的情况。

f[i][j]代表选i个数,数的和在模(m-1)意义下为j的方案数,那么f[i][j]=f[i-1][(j-num[i]+m-1) mod (m-1)]然后这就是个循环卷积,我们对长度为2m的多项式做多项式乘法,然后大于等于m-1的项累加到i mod (m-1)的项上,并清0。

可是那个n很大,我们发现我们只要计算出f[1],最后的多项式就是f[1]^n,快速幂就ok了

#include<bits/stdc++.h>
using namespace std;
#define maxn 200000
const int mo=1004535809;
#define ll long long
int ksm(int a,int b,int c){int res=1;for(;b;b>>=1,a=1LL*a*a%c)if(b&1)res=1LL*res*a%c;return res;}
int n,invn,nn,R[maxn],p,root,x,m;
void NTT(int *a,int f){
    int id=0;
    for(int i=1;i<nn;i++)if(i<R[i])swap(a[i],a[R[i]]);
    for(int i=1;i<nn;i<<=1){
        id++;
        int wn=ksm(3,f==1?(mo-1)/(1<<id):mo-1-(mo-1)/(1<<id),mo);
        for(int j=0;j<nn;j+=(i<<1)){
            int w=1;
            for(int k=0;k<i;k++){
                int x=a[j+k],y=(1LL*w*a[j+k+i])%mo;
                a[j+k]=1LL*(x+y)%mo,
                a[j+k+i]=(1LL*(x-y)%mo+mo)%mo,
                w=(1LL*w*wn)%mo;
            }
        }
    }
    if(f==-1)for(int i=0;i<nn;i++)a[i]=1LL*a[i]*invn%mo;
}
int T[maxn],mm;
int aa[maxn],bb[maxn],cc[maxn],dd[maxn],pos[maxn];
inline bool judge(int x, int p){
    for (int i=2;i*i<=p;i++)
        if ((p-1)%i==0&&ksm(x,(p-1)/i,p)==1)return 0;
    return 1;
}
inline int Find_Root(int p){
    if(p==2)return 1;
    int res = 2;
    for(;!judge(res,p);res++);
    return res;
}
int num[maxn];
void init(){
    scanf("%d%d%d%d",&n,&p,&x,&m);
    for(int i=1;i<=m;i++)scanf("%d",&T[i]);
    mm=p*2-2;
    int l=0;for(mm=p*2-2,nn=1;nn<=mm;nn<<=1)l++;
    for(int i=1;i<nn;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(l-1));
    invn=ksm(nn,mo-2,mo);
    root=Find_Root(p);
    for(int i=0;i<p-1;i++)num[i]=!i?1:num[i-1]*root%p,pos[num[i]]=i;
}
void mul(int *ret,int *num1,int *num2){
    for(int i=0;i<nn;i++)cc[i]=num1[i],dd[i]=num2[i];
    NTT(cc,1);NTT(dd,1);
    for(int i=0;i<nn;i++)ret[i]=(1LL*cc[i]*dd[i])%mo;
    NTT(ret,-1);
    for(int i=nn-1;i>=p-1;i--){
        ret[i-p+1]=1LL*(ret[i-p+1]+ret[i])%mo;
        ret[i]=0;
    }
}
void quickpow2(int *a,int b){
    aa[0]=1;for(;b;b>>=1,mul(a,a,a))if(b&1)mul(aa,a,aa);
}
void solve(){
    for(int i=1;i<=m;i++){
        if(T[i]==0)continue;
        bb[pos[T[i]]]++;
    }
    quickpow2(bb,n);
    int ans=aa[pos[x]];
    printf("%d\n",ans);
}
int main(){
    init();
    solve();
    return 0;
}
posted @ 2018-08-08 08:35  lnyzo  阅读(56)  评论(0)    收藏  举报