在学习了FFT之后,我们开始学习NTT,感觉在数论题当中,NTT的应用更广,因为不用考虑精度问题,我们可以做数域更广的计算。
下面看一下FFT和NTT的区别:
FFT使用单位根作为点值,而NTT使用原根作为点值。原根在数论情况下和单位根的作用是一样的。但是很遗憾的是NTT对于模数有要求。只有在2的次幂足够大的情况下,我才能使用NTT。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod 998244353
using namespace std;
long long a[2097253],b[2097153];
int n,m,L,l,p[2097253];
long long qui(long long a,int x){
long long ret=1;
while(x!=0){
if((x&1)==1) ret=ret*a%mod;
a=a*a%mod;
x=x>>1;
}
return ret;
}
void ntt(long long *g,int type){
for(int i=0;i<L;i++) if(i<p[i]) swap(g[i],g[p[i]]);
for(int i=1;i<L;(i<<=1)){
long long wn=qui(3,(mod-1)/(i<<1));
for(int j=0;j<L;j+=(i<<1)){
long long w=1;
for(int k=j;k<j+i;w=w*wn%mod,k++){
long long t=g[k+i]*w%mod;
g[k+i]=(g[k]-t+mod)%mod;g[k]=(g[k]+t)%mod;
}
}
}
if(type==1) return;reverse(g+1,g+L);
long long ni=qui(L,mod-2);
for(int i=0;i<L;i++) g[i]=g[i]*ni%mod;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lld",&a[i]);
for(int i=0;i<=m;i++) scanf("%lld",&b[i]);
for(L=1;L<=n+m;l++,L=L<<1);
for(int i=1;i<L;i++) p[i]=((p[i>>1]>>1)|((i&1)<<(l-1)));
ntt(a,1);ntt(b,1);
for(int i=0;i<L;i++) a[i]=a[i]*b[i]%mod;
ntt(a,-1);
for(int i=0;i<=n+m;i++) printf("%lld ",a[i]);
}
而重头戏其实在后面,我们还要学习多项式一家: