[ DP NTT ] [ SDOI2015 ] BZOJ3992

fi,j 表示前 i 个数乘积为 j 的方案数。然后可以写出DP方程。
由于 m <script type="math/tex" id="MathJax-Element-211">m</script> 是质数,可以用原根使乘法转换为加法,然后用快速幂+NTT加速即可。

#include<bits/stdc++.h>
using namespace std;
const int N=16400;
const int P=1004535809;
const int G=3;
int k,n,m,p,M;
int ind[N];
int t[N],l,x,y;
int a[N],b[N],r[N];
inline void Add(int& x,int y){
    x=(x+y)%P;
}
inline int Pow(int x,int y,int z){
    int Ans=1;
    for(;y;y>>=1,x=1ll*x*x%z)
    if(y&1)Ans=1ll*Ans*x%z;
    return Ans;
}
inline int Getg(int n){
    for(int i=2;i<n-1;i++)if(!((n-1)%i))t[++l]=i;
    for(int i=2;;i++){
        int j=1;
        for(;j<=l;j++)if(Pow(i,t[j],n)==1)break;
        if(j>l)return i;
    }
}
inline void NTT(int* a,int n,int d){
    for(int i=0;i<n;i++)if(r[i]>i)swap(a[i],a[r[i]]);
    for(int i=1;i<n;i<<=1){
        int wn=Pow(G,d==1?(P-1)/(i<<1):P-1-(P-1)/(i<<1),P);
        for(int j=0;j<n;j+=i<<1){
            int w=1;
            for(int k=0;k<i;k++){
                int x=a[j+k],y=1ll*a[j+k+i]*w%P;
                a[j+k]=(x+y)%P;a[j+k+i]=(x-y)%P;
                w=1ll*w*wn%P;
            }
        }
    }
    if(d==-1){
        int inv=Pow(n,P-2,P);
        for(int i=0;i<n;i++)a[i]=1ll*a[i]*inv%P;
    }
}
int main(){
    scanf("%d%d%d%d",&n,&m,&y,&p);
    for(int i=0,j=1,g=Getg(m);i<m-1;i++,j=j*g%m)ind[j]=i;
    while(p--){
        scanf("%d",&x);
        if(x)a[ind[x]]=1;
    }
    m--;
    for(l=0,M=1;M<(m<<1);M<<=1)l++;
    for(int i=0;i<M;i++)r[i]=(r[i>>1]>>1)|((i&1)<<l-1);
    b[0]=1;
    for(;n;n>>=1){
        NTT(a,M,1);
        if(n&1){
            NTT(b,M,1);
            for(int i=0;i<M;i++)b[i]=1ll*b[i]*a[i]%P;
            NTT(b,M,-1);
            for(int i=m;i<M;i++)Add(b[i%m],b[i]),b[i]=0;
        }
        for(int i=0;i<M;i++)a[i]=1ll*a[i]*a[i]%P;
        NTT(a,M,-1);
        for(int i=m;i<M;i++)Add(a[i%m],a[i]),a[i]=0;
    }
    cout<<(b[ind[y]]+P)%P<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值