[SDOI2015] 序列统计 题解

乘法并不容易用 FFT 或 NTT 维护,考虑在模意义下化乘为加。

化乘为加主要有两种方法:\(\log\)\(\gamma\)(指标),由于在取模意义下,所以使用后者。

那剩下的部分就是快速幂,用 NTT 加速即可。时间复杂度 \(O(m\log m\log n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=50005,p=1004535809;
namespace NTT{
    int rev[N],mx,k,m,qp,h;
    struct dft{int fg[N];};
    int qpow(int x,int y,int p){
        int re=1;
        while(y){
            if(y&1) re=re*x%p;
            x=x*x%p,y>>=1;
        }return re;
    }void init(int n){
        mx=1,k=0,rev[0]=0,h=m-1;
        while(mx<=n) mx*=2,k++;
        for(int i=0;i<mx;i++)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
        qp=qpow(mx,p-2,p);
    }void fmd(dft &a){
        for(int i=m-1;i<mx;i++) if(a.fg[i])
            a.fg[i%h]=(a.fg[i%h]+a.fg[i])%p,a.fg[i]=0;
    }void ntt(dft &a,int fl){
        for(int i=0;i<mx;i++)
            if(i<rev[i]) swap(a.fg[i],a.fg[rev[i]]);
        for(int i=1;i<mx;i*=2){
            int om=qpow(fl?3:(p+1)/3,(p-1)/(i<<1),p);
            for(int j=0,w=1;j<mx;j+=i*2,w=1)
                for(int k=j;k<j+i;k++,w=w*om%p){
                    int x=a.fg[k],y=w*a.fg[k+i]%p;
                    a.fg[k]=(x+y)%p,a.fg[k+i]=(x-y+p)%p;
                }
        }if(fl) return;
        for(int i=0;i<mx;i++)
            a.fg[i]=a.fg[i]*qp%p;
    }
}using namespace NTT;
int n,ed,ln,g,ru[N];
dft re,now;vector<int>pr;
int prirt(int x){
    x--;int cc=x;
    for(int i=2;i*i<=x;i++){
        if(cc%i==0) pr.push_back(i);
        while(cc%i==0) cc/=i;
        if(cc==1) continue;
    }if(cc>1) pr.push_back(cc);
    for(int i=1,fl=1;i<=x;i++,fl=1){
        for(auto j:pr)
            if(qpow(i,x/j,m)==1){fl=0;break;}
        if(fl) return i;
    }return 0;
}signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m>>ed>>ln,init(m*2);
    re.fg[0]=1,g=prirt(m);
    for(int i=1,mi=1;i<m;i++)
        ru[mi]=i-1,mi=mi*g%m;
    for(int i=1,x;i<=ln;i++){
        cin>>x;if(!x) continue;
        now.fg[ru[x]]=1;
    }while(n){
        ntt(now,1);
        if(n&1){
            ntt(re,1);
            for(int i=0;i<mx;i++)
                re.fg[i]=re.fg[i]*now.fg[i]%p;
            ntt(re,0),fmd(re);
        }for(int i=0;i<mx;i++)
            now.fg[i]=now.fg[i]*now.fg[i]%p;
        ntt(now,0),fmd(now),n>>=1;
    }cout<<re.fg[ru[ed]];
    return 0;
}
posted @ 2025-01-21 09:04  长安一片月_22  阅读(8)  评论(0)    收藏  举报