仙人NTT的入门
在快速傅里叶变换中,我们利用
单位复数根实现了消去引理和折半引理
但是由于复数运算的关系,导致精度问题,使人十分捉鸡
那么,有没有什么整数也满足消去引理和折半引理来代替
单位复数根呢?
这就是所谓的原根
那么什么是原根呢?
定义
的原根为满足
的整数
对于一个质数
,如果它存在原根,那么
那么我们用
生成的横坐标就不会相同,以此来生成点集表示法
现在我们用
来代替
因为我们想利用整数进行计算,那么
应该为整数,因为当
为质数时,
,那么
消去引理:
* 证明:
折半引理: 若
为偶数,则
次单位复数根的平方的集合就是
次单位复数根的集合,特别的,每个
次单位复数根出现两次
* 证明:
[消去引理]
非常nice
所以就和FFT一样一样的啦!
板子!
多项式乘法
#include <bits/stdc++.h>
using namespace std;
const int N = 3000010;
const int G=3,mod=(119<<23)+1;
int n,m,l;
int a[N],b[N],r[N];
int ksm(int a,int b) {
int ans=1;
while(b) {
if(b&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
void ntt(int *now,int f) {
for(int i=0;i<n;++i)
if(i<r[i]) swap(now[i],now[r[i]]);
for(int i=1;i<n;i<<=1) {
int gn=ksm(G,(mod-1)/(i<<1));
for(int j=0;j<n;j+=(i<<1)) {
int x,y,g=1;
for(int k=0;k<i;++k,g=1ll*g*gn%mod) {
x=now[j+k],y=1ll*g*now[j+k+i]%mod;
now[j+k]=(x+y)%mod;
now[j+k+i]=(x-y+mod)%mod;
}
}
}
if(f!=1) {//在FFT中我们变换了回来,但是NTT就需要单独变换
int ny=ksm(n,mod-2);
reverse(a+1,a+n);
for(int i=0;i<n;++i) a[i]=1ll*a[i]*ny%mod;
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=0;i<=n;++i) scanf("%d",&a[i]);
for(int i=0;i<=m;++i) scanf("%d",&b[i]);
m+=n;
for(n=1;n<=m;n<<=1) ++l;
for(int i=0;i<n;++i)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
ntt(a,1);ntt(b,1);
for(int i=0;i<n;++i) a[i]=1ll*a[i]*b[i]%mod;
ntt(a,-1);
for(int i=0;i<=m;++i)
printf("%d ",a[i]);
return 0;
}