1. 程式人生 > 實用技巧 >任意模數 NTT

任意模數 NTT

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1<<21,MOD1=998244353,MOD2=1004535809,MOD3=469762049;
inline ll fpow(ll a,ll x,ll mod) { ll ans=1; for(;x;x>>=1,a=a*a%mod) if(x&1) ans=ans*a%mod; return ans; }
inline ll inv(ll a,ll mod) { return fpow(a,mod-2,mod); }
ll L,Rev[MAXN],invL1,invL2,invL3;
inline void preNTT(){
    Rev[1]=L>>1;
    for(int i=2;i<L;i++) Rev[i]=(Rev[i>>1]>>1)|Rev[i&1];
    invL1=inv(L,MOD1);
    invL2=inv(L,MOD2);
    invL3=inv(L,MOD3);
}
inline void NTT(int f[MAXN][4],ll op){
    ll g1=(op==1?3:inv(3,MOD1)),g2=(op==1?3:inv(3,MOD2)),g3=(op==1?3:inv(3,MOD3));
    for(int i=1;i<L;i++) if(i<Rev[i])
        for(int j=1;j<=3;j++) swap(f[i][j],f[Rev[i]][j]);
    for(int bas=2,mid=1,lb=1;bas<=L;bas<<=1,mid<<=1,lb++)
        for(int l=0,omega1=fpow(g1,MOD1>>lb,MOD1),omega2=fpow(g2,MOD2>>lb,MOD2),omega3=fpow(g3,MOD3>>lb,MOD3);l<L;l+=bas)
            for(int k=l,buf1=1,buf2=1,buf3=1,tmp1,tmp2,tmp3;
            tmp1=1ll*buf1*f[k+mid][1]%MOD1,tmp2=1ll*buf2*f[k+mid][2]%MOD2,tmp3=1ll*buf3*f[k+mid][3]%MOD3,k<mid+l;
            k++,buf1=1ll*buf1*omega1%MOD1,buf2=1ll*buf2*omega2%MOD2,buf3=1ll*buf3*omega3%MOD3)
                    f[k+mid][1]=(0ll+f[k][1]-tmp1)%MOD1,f[k][1]=(0ll+f[k][1]+tmp1)%MOD1,
                    f[k+mid][2]=(0ll+f[k][2]-tmp2)%MOD2,f[k][2]=(0ll+f[k][2]+tmp2)%MOD2,
                    f[k+mid][3]=(0ll+f[k][3]-tmp3)%MOD3,f[k][3]=(0ll+f[k][3]+tmp3)%MOD3;
    if(op==1){
        for(int i=0;i<L;i++)
            f[i][1]=(0ll+f[i][1]+MOD1)%MOD1,f[i][2]=(0ll+f[i][2]+MOD2)%MOD2,f[i][3]=(0ll+f[i][3]+MOD3)%MOD3;
    }
    else{
        for(int i=0;i<L;i++)
            f[i][1]=(0ll+f[i][1]+MOD1)*invL1%MOD1,f[i][2]=(0ll+f[i][2]+MOD2)*invL2%MOD2,f[i][3]=(0ll+f[i][3]+MOD3)*invL3%MOD3;
    }
}
inline void mul(int f[MAXN][4],int g[MAXN][4],int h[MAXN][4],int N){
    static int p[MAXN][4],q[MAXN][4];
    L=1;
    while(L<(N<<1)) L<<=1;
    preNTT();
    for(int i=0;i<L;i++)
        p[i][1]=f[i][1],q[i][1]=g[i][1],
        p[i][2]=f[i][2],q[i][2]=g[i][2],
        p[i][3]=f[i][3],q[i][3]=g[i][3];
    NTT(p,1);
    NTT(q,1);
    for(int i=0;i<L;i++)
        h[i][1]=1ll*p[i][1]*q[i][1]%MOD1,
        h[i][2]=1ll*p[i][2]*q[i][2]%MOD2,
        h[i][3]=1ll*p[i][3]*q[i][3]%MOD3;
    NTT(h,-1);
}
inline int crt(ll a1,ll m1,ll a2,ll m2,ll a3,ll m3,ll p){
    __int128 mod=1,sum=0; mod=mod*m1*m2*m3;
    sum+=mod/m1*inv(mod/m1%m1,m1)*a1;
    sum+=mod/m2*inv(mod/m2%m2,m2)*a2;
    sum+=mod/m3*inv(mod/m3%m3,m3)*a3;
    return sum%mod%p;
}

int F[MAXN][4],G[MAXN][4],H[MAXN][4],N,M,P;
inline void work(){
    cin>>N>>M>>P;
    for(int i=0;i<=N;i++) cin>>F[i][0],F[i][3]=F[i][2]=F[i][1]=F[i][0];
    for(int i=0;i<=M;i++) cin>>G[i][0],G[i][3]=G[i][2]=G[i][1]=G[i][0];
    mul(F,G,H,N+M+2);
    for(int i=0;i<L;i++) H[i][0]=crt(H[i][1],MOD1,H[i][2],MOD2,H[i][3],MOD3,P);
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    work();
    for(int i=0;i<=N+M;i++) cout<<H[i][0]<<" ";
    cout<<endl;
    return 0;
}