先贴三个写的很棒的博客
https://blog.csdn.net/enjoy_pascal/article/details/81478582
https://www.cnblogs.com/RabbitHu/p/FFT.html
https://zhuanlan.zhihu.com/p/31584464?from_voters_page=true
然后整理一下fft的板子,避免以后忘记了怎么敲的在看不懂dalao们的板子
根据定义直接写的朴素版
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1);
const int mod=1000000009;
/****************************Link Start****************************/
struct Complex {
double a,b;
Complex operator+(const Complex &x) const {
return Complex{a+x.a,b+x.b};
}
Complex operator-(const Complex &x) const {
return Complex{a-x.a,b-x.b};
}
Complex operator*(const Complex &x) const {
return Complex{a*x.a-b*x.b,a*x.b+b*x.a};
}
};
Complex fx[100010],gx[100010];
Complex hx[100010];
Complex tmp[100010];
void fft(Complex arr[],int len,int cal) {
if(len==1) return ;
int mid=len/2;
for(int i=0;i<mid;i++) tmp[i]=arr[i*2],tmp[i+mid]=arr[i*2+1];
for(int i=0;i<len;i++) arr[i]=tmp[i];
fft(arr,mid,cal); fft(arr+mid,mid,cal);
for(int i=0;i<mid;i++) {
Complex omg={cos(2*pi*i/len),cal*sin(2*pi*i/len)};
tmp[i]=arr[i]+(omg*arr[i+mid]);
tmp[i+mid]=arr[i]-(omg*arr[i+mid]);
}
for(int i=0;i<len;i++) arr[i]=tmp[i];
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=0,x;i<n;i++) {
scanf("%d",&x); fx[i].a=1.0*x;
}
for(int i=0,x;i<m;i++) {
scanf("%d",&x); gx[i].a=1.0*x;
}
int len=1;
while(len<n+m-1) len*=2;
fft(fx,len,1); fft(gx,len,1);
for(int i=0;i<len;i++) hx[i]=fx[i]*gx[i];
fft(hx,len,-1);
for(int i=0;i<n+m-1;i++) {
if(i==0) printf("%d",(int)(hx[i].a/len));
else printf(" %d",(int)(hx[i].a/len));
}
return 0;
}
优化后的板子
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll llinf=0x3f3f3f3f3f3f3f3f;
const double pi=acos(-1);
const int mod=1000000009;
/****************************Link Start****************************/
struct Complex {
double a,b;
Complex operator+(const Complex &x) const {
return Complex{a+x.a,b+x.b};
}
Complex operator-(const Complex &x) const {
return Complex{a-x.a,b-x.b};
}
Complex operator*(const Complex &x) const {
return Complex{a*x.a-b*x.b,a*x.b+b*x.a};
}
};
Complex fx[100010],gx[100010];
Complex hx[100010];
int pos[100010];
void fft(Complex arr[],int len,int cal) {
int bit=0;
while((1<<(bit+1))<len) bit++;
pos[0]=0;
for(int i=0;i<len;i++) {
pos[i]=(pos[i>>1]>>1)|((i&1)<<bit);
if(i<pos[i]) swap(arr[i],arr[pos[i]]);
}
for(int mid=1;mid<len;mid*=2) {
for(int i=0;i<len;i+=2*mid) for(int j=0;j<mid;j++) {
Complex omg={cos(pi*j/mid),cal*sin(pi*j/mid)};
Complex x=arr[i+j],y=arr[i+j+mid];
arr[i+j]=x+omg*y,arr[i+j+mid]=x-omg*y;
}
}
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=0,x;i<n;i++) {
scanf("%d",&x); fx[i].a=1.0*x;
}
for(int i=0,x;i<m;i++) {
scanf("%d",&x); gx[i].a=1.0*x;
}
int len=1;
while(len<n+m-1) len*=2;
fft(fx,len,1); fft(gx,len,1);
for(int i=0;i<len;i++) hx[i]=fx[i]*gx[i];
fft(hx,len,-1);
for(int i=0;i<n+m-1;i++) {
if(i==0) printf("%d",(int)(hx[i].a/len));
else printf(" %d",(int)(hx[i].a/len));
}
return 0;
}
最后是感觉需要记录下来的一些东西
1、fft本质是以nlogn的复杂度将多项式的系数表示转换成点值表示,同样ifft(快速傅里叶逆变换)可以nlogn的复杂度把多项式的点值表示转成系数表示
点值表示的两个多项式可以直接o(n)对应相乘
2、跑fft的多项式的项数需要是2的整数次幂,长度不够的要系数补0进行补齐,举个实际的栗子:
设h(x)=f(x)*g(x),在一般系数表达式中f(x)的项数为n,g(x)的项数为m,则h(x)的项数应该为n+m-1
那么我们拿f(x)和g(x)来跑fft时构造的多项式的项数应该是n+m-1补齐之后的2的整数次幂,而不是n或m补齐的
3、优化版板子没有用递归写法,而是直接用递推式求出各系数分治之后的位置,从下往上一层一层的计算
最最后,啥是蝴蝶操作啊