「SDOI2015」序列统计

题目链接:Click here

Solution:

容易得到这样一个(dp),设(f[i][j])表示已经选了(i)个数,乘积(mod \,\,m)后为(j)的方案

[f[2 imes i][j]=sum_{a imes bequiv j\,\,(mod\,\, m)} f[i][a] imes f[i][b] ]

考虑如何优化这个转移方程,注意到这道题的模数为(1004535809),这是一个很大的提示,考虑(NTT)

但是我们知道(NTT)处理的是卷积,而这里是指数相乘,该怎么转化呢?

乘法变加法,不由得让我们想到对数,模意义下的对数,又不由得让我们想到原根

考虑原根的定义,(g)(p)的原根,则(g^0 ,g^1dots g^{p-2})(mod \,\,p)下恰好取到([1,p-1])的所有整数

原根也很好求,把(varphi(p)=prod pr_i^{a_i}),然后对于所有的(pr_i)都有(g^{varphi(p) over pr_i} ot equiv 1 \,\, ( mod \,\, p))

那么我们令(j=log_g j,a=log_g a,b=log_g b),同时根据扩展欧拉定理,转移方程就变成了

[f[2 imes i][j]=sum_{a+b equiv j \,\, (mod \,\, varphi(m))}f[i][a] imes f[i][b] ]

注意每次转移后(f[i][j]+=f[i][j+varphi(m)]),我们就可以愉快的(NTT)啦!

等等,好像还不行,(n=1e9),仔细观察这个转移,发现可以直接多项式快速幂,那么再快速幂即可,时间复杂度(O(mlog m log n))

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e4+11;
const int mod=1004535809;
int n,m,S,X,len=1,tim;
int f[N],v[N],p[N],pr[N],lg[N];
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int qpow(int x,int y,int p){
    int re=1;
    while(y>0){
        if(y&1) re=re*x%p;
        y>>=1;x=x*x%p;
    }return re;
}
int GetRt(int x){
    int tot=0,phi=x-1;
    for(int i=2;i*i<=phi;i++)
        if(phi%i==0){
            pr[++tot]=i;
            while(phi%i==0) phi/=i;
        }
    if(phi>1) pr[++tot]=phi;
    phi=x-1;
    for(int i=2;i<=phi;i++){
        int flag=1;
        for(int j=1;j<=tot&&flag;j++)
            if(qpow(i,phi/pr[j],x)==1) flag=0;
        if(flag) return i;
    }
    return -1;
}
void NTT(int *a,int flag){
    for(int i=0;i<len;i++)
        if(i<p[i]) swap(a[i],a[p[i]]);
    for(int l=2;l<=len;l<<=1){
        int wn=qpow(3,(mod-1)/l,mod);
        if(flag==-1) wn=qpow(wn,mod-2,mod);
        for(int st=0;st<len;st+=l){
            int w=1;
            for(int u=st;u<st+(l>>1);u++,w=w*wn%mod){
                int x=a[u],y=w*a[u+(l>>1)]%mod;
                a[u]=(x+y)%mod;a[u+(l>>1)]=(x+mod-y)%mod;
            }
        }
    }
}
void Mul(int *A,int *B,int *C){
    static int a[N],b[N];
    for(int i=0;i<len;i++) a[i]=A[i];
    for(int i=0;i<len;i++) b[i]=B[i];
    NTT(a,1);NTT(b,1);
    for(int i=0;i<len;i++) a[i]=a[i]*b[i]%mod;
    NTT(a,-1);int inv=qpow(len,mod-2,mod);
    for(int i=0;i<len;i++) a[i]=a[i]*inv%mod;
    for(int i=0;i<m-1;i++) C[i]=(a[i]+a[i+m-1])%mod;
}
signed main(){
    n=read(),m=read(),X=read(),S=read();
    while(len<(m<<1)) len<<=1,++tim;
    for(int i=0;i<len;i++)
        p[i]=(p[i>>1]>>1)|((i&1)<<(tim-1));
    int g=GetRt(m);
    for(int i=0;i<m-1;i++) lg[qpow(g,i,m)]=i;
    for(int i=1;i<=S;i++){
        int x=read()%m;
        if(x) ++v[lg[x]];
    }
    f[lg[1]]=1; 
    while(n){
        if(n&1) Mul(f,v,f);
        n>>=1;Mul(v,v,v);
    }
    printf("%lld
",f[lg[X]]);
    return 0;
}

原文地址:https://www.cnblogs.com/NLDQY/p/12242664.html