【算法简介】
FFT主要用于处理多项式乘法的问题,时间复杂度可以优化到O(NlogN)
整体思路是把两个多项式转换为点值表达,然后用O(N)进行乘法
这个问题的瓶颈就在于如何用更快的方式转换为点值
这里就要引入傅里叶的方法了:
我们取一些特殊的点计算点值,也就是把复数的单位圆平均分成n分的值带入
然后我们再针对这个进行加速,也就是快速傅里叶变换
这样我们就可以证明在N*logN的复杂度进行一些操作
继续优化:把数组分到最后的位置,我们可以发现一些规律
如果一个点位置在i,那么它最后的位置就在它二进制的取反的位置
比如
6(110)-> 3(011)
利用这个性质就可以的到一个非递归的版本
继续优化, 下面就是优美的蝴蝶变换了
其实就是优化掉了buf数组 名字好高级
剩下的就是背代码
【代码】
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define mp make_pair
#define fi first
#define se second
#define lson now<<1
#define rson now<<1|1
typedef long long ll;
const double PI=acos(-1.0);
const int maxn=4e6+5;
int n,m;
struct complex
{
double x,y;
complex (double xx=0,double yy=0)
{
x=xx; y=yy;
}
}a[maxn],b[maxn];
complex operator + (complex a,complex b)
{
return complex(a.x+b.x,a.y+b.y);
}
complex operator - (complex a,complex b)
{
return complex(a.x-b.x,a.y-b.y);
}
complex operator * (complex a,complex b)
{
return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
int lim=1,l,r[maxn];
void fft(complex *A,int op)
{
for(int i=0;i<lim;i++)
if(i<r[i]) swap(A[i],A[r[i]]);
for(int mid=1;mid<lim;mid<<=1)
{
complex Wn(cos(PI/mid),op*sin(PI/mid));
for(int R=mid<<1,j=0;j<lim;j+=R)
{
complex w(1,0);
for(int k=0;k<mid;k++,w=w*Wn)
{
complex x=A[j+k],y=w*A[j+mid+k];
A[j+k]=x+y;
A[j+mid+k]=x-y;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) scanf("%lf",&a[i].x);
for(int j=0;j<=m;j++) scanf("%lf",&b[j].x);
while(lim<=(n+m)) lim<<=1,l++;
for(int i=0;i<lim;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
fft(a,1); fft(b,1);
for(int i=0;i<=lim;i++) a[i]=a[i]*b[i];
fft(a,-1);
for(int i=0;i<=n+m;i++)
printf("%d ",(int)(a[i].x/lim+0.5));
return 0;
}
【例题】P1919 【模板】A*B Problem升级版(FFT快速傅里叶)
NTT
考虑由于单位根的选取,一定会产生精度的问题,所以取模的操作很难进行,且速度较慢
所以NTT应运而生,来解决取模的问题
我们利用原根的性质来构造单位根
设原根为g,可以证明是一个合理的方案
常见原根 : 998244353 ——> 3
......
这样我们就得到了NTT的板子
void ntt(int *A,int op)
{
for(int i=0;i<lim;i++)
if(r[i]<i) swap(A[i],A[r[i]]);
for(int i=1;i<lim;i<<=1)
{
ll Wn;
if(op==1) Wn=qpow(3,(mod-1)/(i<<1));
else Wn=qpow((mod+1)/3,(mod-1)/(i<<1));
for(int R=i<<1,j=0;j<lim;j+=R)
{
ll w=1LL;
for(int k=0;k<i;k++,w=w*Wn%mod)
{
int x=A[j+k],y=1LL*w*A[j+k+i]%mod;
A[j+k]=(x+y)%mod;
A[j+k+i]=(x-y+mod)%mod;
}
}
}
if(op==-1)
{
int inv=qpow(lim,mod-2);
for(int i=0;i<lim;i++) A[i]=1LL*A[i]*inv%mod;
}
}