标签:info 数论 lap https bsp stream ima span 注意
NTT是一个玄学的东西,它通过数论达到和FFT一样的效果(甚至还要快得多)。
NTT的原理其实是将FFT的$w^{k}_{n}$通过另外一个东西代替,从而将FFT中极其之慢的(而且精度爆炸的)浮点数运算更改为整数运算。
在讲NTT之前,先来了解一下数学方面的内容。
若$(a,p)$=1,且$p>1$,对于$a^{n}\equiv1(mod p)$最小的$n$称为$a$模$p$的阶,记为$\delta_{p}(a)$
设正整数$p$,整数$a$,若$\delta_{p}(a)=\phi(p)$则称$a$是模$p$的一个原根
原根有一些有趣的性质,而且跟我们在FFT中用到的单位根用到的性质一样!别问我怎么证
为了避免吐槽,还是放个大佬证明
好的,我们终于将浮点数运算转换到了整数域运算。
所以我们直接将FFT代码中的单位根全部换成原根就阔以了。
(不会FFT的请左转百度或右转我的博客)
话说这种算法都应该背模板
#include<iostream> #include<cmath> using namespace std; typedef long long ll; const int N=1e7+5,P=998244353,P1=3,P2=332748118; int lena,lenb,n=1,lim,r[N]; ll a[N],b[N]; ll rpow(ll x,ll y){//要手打 ll res=1; while(y){ if(y&1)res=(res*x)%P; x=(x*x)%P; y>>=1; } return res%P; } inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘){ if(ch==‘-‘)f=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘){ x=x*10+ch-‘0‘; ch=getchar(); } return x*f; } void NTT(ll *A,int tp){ for(int i=0;i<n;i++)if(i<r[i])swap(A[i],A[r[i]]); for(int i=1;i<n;i<<=1){ ll W=rpow(tp?P1:P2,(P-1)/(i<<1));//替换成原根 for(int j=i<<1,k=0;k<n;k+=j){ ll w=1; for(int l=0;l<i;l++,w=(w*W)%P){//注意模数 int x=A[k+l],y=w*A[k+i+l]%P; A[k+l]=(x+y)%P; A[k+i+l]=(x-y+P)%P; } } } } int main(){ lena=read();lenb=read(); while(n<=lena+lenb)n<<=1,lim++; for(int i=0;i<=lena;i++)a[i]=(read()+P)%P; for(int i=0;i<=lenb;i++)b[i]=(read()+P)%P; for(int i=0;i<n;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lim-1)); NTT(a,1); NTT(b,1); for(int i=0;i<=n;i++)a[i]=(a[i]*b[i])%P; NTT(a,0); ll inv=rpow(n,P-2); for(int i=0;i<=lena+lenb;i++)printf("%d ",(a[i]*inv)%P); }
实测洛谷P3803用FFT 3.14s,NTT 1.76s(不吸氧)
(上为NTT,下为FFT)
标签:info 数论 lap https bsp stream ima span 注意
原文地址:https://www.cnblogs.com/Evan704/p/11409421.html