NTT
一般在运用复数求解FFT的过程中难免不用到三角函数和实数,但是大多数多项式题目要对答案取模。此时fft将不再适用。
现在思考一个问题:我们能不能找到一个东西替代掉复数。
我们回顾一遍fft过程,中心运算有如下:
-
(W_n^1)^k=W_n^k -
W_n^i=W_{n}^{i\%n} -
W_{2n}^{2i}=W_n^i -
W_n^{i+n/2}=-W_n^i -
\sum_{i=0}^{i<n}W_n^i=0
现在有如下构造:
-
在模
mod=k2^n-1 下运算,其中mod为质数(一般可以是998244353) -
设G为原根(理论上随便取一个数,一般是3)
-
然后把这几个定义带回上述式子,会发现都没有问题,并且现在的每一步都可以取模,于是就可以替换掉fft了。
(p.s 关于每一步的证明,啥时候想写了再来补。)
更可幸的是NTT的常数比FFT小(所以FFT各个方面都比不过NTT?)
code (微带封装版)
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int getint(){
int ssum=0,ff=1;char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-') ff=-1,ch=getchar();
for(;isdigit(ch);ch=getchar()) ssum=ssum*10+ch-'0';
return ssum*ff;
}
const int M=5e6+5,G=3,mod=998244353;
inline int ksm(int x,int mi){
int res=1;
while(mi){
if(mi&1) res=res*x%mod;
x=x*x%mod;mi>>=1;
}
return res;
}
int n,m,lim=1,f[M],g[M],tr[M];
void Getlim(){
while(lim<=n+m) lim<<=1;
for(int i=0;i<=n;i++) f[i]=getint();
for(int i=0;i<=m;i++) g[i]=getint();
for(int i=0;i<=lim;i++) tr[i]=((tr[i>>1]>>1)|((i&1)?lim/2:0));
}
void Ntt(int *f,int fl){
for(int i=0;i<lim;i++) if(tr[i]>i) swap(f[i],f[tr[i]]);
for(int p=2;p<=lim;p<<=1){
int mi=ksm(G,(mod-1)/p);
if(fl==-1) mi=ksm(mi,mod-2);
for(int i=0;i<lim;i+=p){
int ori=ksm(mi,i);
for(int j=i;j<i+(p>>1);j++){
int tt=ori*f[j+(p>>1)];
f[j+(p>>1)]=(f[j]-tt)%mod;
f[j]=(f[j]+tt)%mod;
ori=ori*mi%mod;
}
}
}
}
inline void Px(int *f,int *p){
for(int i=0;i<lim;i++) f[i]=f[i]*g[i]%mod;
}
signed main(){
cin>>n>>m;
Getlim();
Ntt(f,1);Ntt(g,1);
Px(f,g);Ntt(f,-1);
int inv=ksm(lim,mod-2);
for(int i=0;i<=n+m;i++) cout<<(f[i]*inv%mod+mod)%mod<<' ';
return 0;
}