Skip to content

三模数NTT ​

假设答案为x,三模数分别为A,B,C

  • $ x \equiv x_1(mod\ A) $
  • $ x \equiv x_2(mod\ B) $
  • $ x \equiv x_3(mod\ C) $

合并前两个,

  • $ x_1+k_1A=x_2+k_2B $
  • $ x_1+k_1A \equiv x_2(mod\ B) $
  • $ k1 \equiv \frac{x_2-x_1}{A}(mod\ B) $

求出 $ x \equiv x_1+k_1A(mod\ AB),x_4=x_1+k_1A(mod\ AB) $

  • $ x_4+k_4AB=x_3+k_3C $
  • $ k_4 \equiv \frac{x_3-x_4}{AB}(mod\ C) $

求出 $ x \equiv x_4+k_4AB(mod\ ABC),x<ABC,x=x_4+k_4AB $

cpp
namespace NTT{
    vector<int> rev;
    int bit=0,tot;
    int qpow(int a,int b,const int mod){
        int ans=1;
        for(;b;b>>=1){
            if(b&1)ans=ans*a%mod;
            a=a*a%mod;
        }
        return ans;
    }
    int inv(int n,int P){
        return qpow(n,P-2,P);
    }
    const int mod1=998244353,mod2=1004535809,mod3=469762049,g=3;
    const int mod1_2=mod1*mod2;
    const int inv1=inv(mod1,mod2),inv2=inv(mod1_2%mod3,mod3);
    struct Int{
        int A,B,C;
        Int(){}
        Int(int val):A(val),B(val),C(val){}
        Int(int _A,int _B,int _C):A(_A),B(_B),C(_C){}
        static Int reduce(const Int &x){
            return Int(x.A+(x.A>>31&mod1),x.B+(x.B>>31&mod2),x.C+(x.C>>31&mod3));
        }
        Int operator + (const Int &t)const{
            return reduce(Int(A+t.A-mod1,B+t.B-mod2,C+t.C-mod3));
        }
        Int operator - (const Int &t)const{
            return reduce(Int(A-t.A,B-t.B,C-t.C));
        }
        Int operator * (const Int &t)const{
            return {A*t.A%mod1,B*t.B%mod2,C*t.C%mod3};
        }
        int val(){
            int x=(B-A+mod2)%mod2*inv1%mod2*mod1+A;
            return ((C-x%mod3+mod3)%mod3*inv2%mod3*(mod1_2%P)%P+x)%P;
        }
    } itot;
    vector<Int> gn;
    void ntt(vector<Int> &a,int inv){
        for(int i=1;i<tot;i++){
            if(i<rev[i])swap(a[i],a[rev[i]]);
        }
        for(int mid=1;mid<tot;mid<<=1){
            const int t=tot/mid>>1;
            for(int i=0;i<tot;i+=mid*2){
                for(int j=0;j<mid;j++){
                    const Int gk=(inv==1?gn[t*j]:gn[tot-t*j]);
                    const Int x=a[i+j],y=gk*a[i+j+mid];
                    a[i+j]=x+y,a[i+j+mid]=x-y;
                }
            }
        }
        if(inv==-1){
            for(int i=0;i<tot;i++)a[i]=a[i]*itot;
        }
    }
    void init(int n){
        bit=0;
        while((1<<bit)<n)bit++;
        tot=1<<bit;
        itot={inv(tot,mod1),inv(tot,mod2),inv(tot,mod3)};
        rev.resize(tot);
        gn.resize(tot+1);
        const Int t={qpow(g,(mod1-1)/tot,mod1),qpow(g,(mod2-1)/tot,mod2),qpow(g,(mod3-1)/tot,mod3)};
        gn[0]=Int(1);
        for(int i=0;i<tot;i++){
            rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
            gn[i+1]=gn[i]*t;
        }
    }
    void mul(vector<Int> &a,vector<Int> &b,int n,int m){
        init(n+m+1);
        a.resize(tot,Int(0));
        b.resize(tot,Int(0));
        ntt(a,1),ntt(b,1);
        for(int i=0;i<tot;i++)a[i]=a[i]*b[i];
        ntt(a,-1);
    }
}
using NTT::Int;
void solve(){
    int n,m;
    cin>>n>>m>>P;
    vector<Int> a(n+1),b(m+1);
    for(int i=0;i<=n;i++){
        int x;
        cin>>x;
        a[i]=Int(x%P);
    }
    for(int i=0;i<=m;i++){
        int x;
        cin>>x;
        b[i]=Int(x%P);
    }
    NTT::mul(a,b,n,m);
    for(int i=0;i<=n+m;i++)cout<<a[i].val()<<" ";
}