快速数论变换ntt

// luogu-judger-enable-o2·
/*(
 ntt 模板
 luogu P3803
in:
 n , m
 0,...,a[n]
 0,...,b[m]

example:
 1 2
 1 2
 1 2 1
 1 4 5 2
*/
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define int long long
void in(int &x){
    x=0;char c=getchar();
    long long y=1;
    while(c<'0'||c>'9'){if(c=='-')y=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    x*=y;
}
void o(int x){
    if(x<0){x=-x;putchar('-');}
    if(x>9)o(x/10);
    putchar(x%10+'0');
}

const int _ = 1e7+10;
const int mod = 998244353;//模数
const int G = 3;//原根
int ppo(int x,int p){
    int t=x,res=1;
    while(p>0){
        if(p&1)res=(res*t)%mod;
        t=(t*t)%mod;
        p>>=1;
    }
    return res%mod;
}
int n,m;
int limit = 1 , bit;
int a[_],b[_];
int r[_];
void ntt(int *A,int type)
{
    for(int i=0;i<limit;i++)if(i<r[i])swap(A[i],A[r[i]]);
    for(int mid = 1;mid < limit;mid<<=1){//当前区间的一半
        int wn  = ppo(type==1 ? G:ppo(G,mod-2),(mod-1)*ppo(mid<<1,mod-2)%mod);
        for(int R = mid << 1,j = 0;j < limit;j+=R){//回溯区间大小 最大不超过 limit
            int w = 1;
            for(int k = 0 ;k<mid;k++,w=w*wn%mod){
                int x = A[j+k],y = w*A[j+mid+k]%mod;
                A[j+k]=x+y,A[j+k]%=mod;
                A[j+k+mid]=x-y+mod,A[j+k+mid]%=mod;
            }
        }

    }
}
signed main() {
    in(n);in(m);
    for(int i = 0;i <= n;i++)cin>>(a[i]),a[i]=(a[i]+mod)%mod;
    for(int j = 0;j <= m;j++)cin>>(b[j]),b[j]=(b[j]+mod)%mod;
    while (limit<=n+m)limit<<=1,bit++;
    for(int i = 0 ;i<limit;i++){
        r[i]=(r[i>>1]>>1)|((i&1)<<(bit-1));//反转序列
    }
    ntt(a,1);
    ntt(b,1);
    for(int i=0;i<=limit;i++)a[i]=a[i]*b[i]%mod;//y;
    ntt(a,-1);
    int inv = ppo(limit,mod-2);//n 的逆元  除n运算
    for(int i=0;i<=n+m;i++){
        printf("%lld ",a[i]*inv%mod);
    }
    return 0;
}
posted @ 2021-07-23 14:43  yesuweiYYYY  阅读(39)  评论(0编辑  收藏  举报