FFT(Fast Fourier Transformation)是离散傅氏变换(DFT)的快速算法。即为快速傅氏变换。它是根据离散傅氏变换的奇、偶、虚、实等特性,对离散傅立叶变换的算法进行改进获得的。

       最近学习FFT,感觉​​GGN_2015的这篇博客​​写的非常好。大家可以去看一看,那我就负责讲一讲题目怎么做吧(一切源于这篇博客)。

      ​​ 一篇好文章​​。

       先贴一段FFT模板。


#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<complex>
using namespace std;
typedef complex<double> cd;//复数类的定义
const int maxl=2094153;//nlogn的最大长度(来自leo学长的博客)
const double PI=3.14159265358979;//圆周率,不解释
cd a[maxl],b[maxl];//用于储存变换的中间结果
int rev[maxl];//用于储存二进制反转的结果
void getrev(int bit){
for(int i=0;i<(1<<bit);i++)//高位决定二进制数的大小
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));//能保证(x>>1)<x,满足递推性质
}
void fft(cd* a,int n,int dft)//变换主要过程
{
for(int i=0;i<n;i++)//按照二进制反转
if(i<rev[i])//保证只把前面的数和后面的数交换,(否则数组会被翻回来)
swap(a[i],a[rev[i]]);
for(int step=1;step<n;step<<=1)//枚举步长的一半
{
cd wn=exp(cd(0,dft*PI/step));//计算单位复根
for(int j=0;j<n;j+=step<<1)//对于每一块
{
cd wnk(1,0);//!!每一块都是一个独立序列,都是以零次方位为起始的
for(int k=j;k<j+step;k++)//蝴蝶操作处理这一块
{
cd x=a[k];
cd y=wnk*a[k+step];
a[k]=x+y;
a[k+step]=x-y;
wnk*=wn;//计算下一次的复根
}
}
}
if(dft==-1)//如果是反变换,则要将序列除以n
for(int i=0;i<n;i++)a[i]/=n;
}
int output[maxl];
char s1[maxl],s2[maxl];
int main()
{
scanf("%s%s",s1,s2);//读入两个数
int l1=strlen(s1),l2=strlen(s2);//就算"次数界"
int bit=1,s=2;//s表示分割之前整块的长度
for(bit=1;(1<<bit)<l1+l2-1;bit++)s<<=1;//找到第一个二的整数次幂使得其可以容纳这两个数的乘积
for(int i=0;i<l1;i++)//第一个数装入a
a[i]=(double)(s1[l1-i-1]-'0');
for(int i=0;i<l2;i++)//第二个数装入b
b[i]=(double)(s2[l2-i-1]-'0');
getrev(bit);fft(a,s,1);fft(b,s,1);//dft
for(int i=0;i<s;i++)a[i]*=b[i];//对应相乘
fft(a,s,-1);//idft
for(int i=0;i<s;i++)//还原成十进制数
{
output[i]+=(int)(a[i].real()+0.5);//注意精度误差
output[i+1]+=output[i]/10;
output[i]%=10;
}
int i;
for(i=l1+l2;!output[i]&&i>=0;i--);//去掉前导零
if(i==-1)printf("0");//特判长度为0的情况
for(;i>=0;i--)//输出这个十进制数
printf("%d",output[i]);
putchar('\n');
return 0;
}

       让我们去掉注释再精简一下。


#include<bits/stdc++.h>
using namespace std;
typedef complex<double> cd;
const int maxl=2094153;
const double PI=3.14159265358979;
cd a[maxl],b[maxl];
int rev[maxl],output[maxl];
char s1[maxl],s2[maxl];
void getrev(int bit)
{
for(int i=0;i<(1<<bit);i++)
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
}
void fft(cd* a,int n,int dft)
{
for(int i=0;i<n;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int step=1;step<n;step<<=1)
{
cd wn=exp(cd(0,dft*PI/step));
for(int j=0;j<n;j+=step<<1)
{
cd wnk(1,0);
for(int k=j;k<j+step;k++)
{
cd x=a[k];
cd y=wnk*a[k+step];
a[k]=x+y;
a[k+step]=x-y;
wnk*=wn;
}
}
}
if(dft==-1)
for(int i=0;i<n;i++)a[i]/=n;
}
int main()
{
scanf("%s%s",s1,s2);
int l1=strlen(s1),l2=strlen(s2);
int bit=1,s=2;
for(bit=1;(1<<bit)<l1+l2-1;bit++)s<<=1;
for(int i=0;i<l1;i++)
a[i]=(double)(s1[l1-i-1]-'0');
for(int i=0;i<l2;i++)
b[i]=(double)(s2[l2-i-1]-'0');
getrev(bit);fft(a,s,1);fft(b,s,1);
for(int i=0;i<s;i++)a[i]*=b[i];
fft(a,s,-1);
for(int i=0;i<s;i++)
{
output[i]+=(int)(a[i].real()+0.5);
output[i+1]+=output[i]/10;
output[i]%=10;
}
int i;
for(i=l1+l2;!output[i]&&i>=0;i--);
if(i==-1)printf("0");
for(;i>=0;i--)printf("%d",output[i]);
putchar('\n');
return 0;
}

       没啥好讲的,只是为了以后容易整理。我也懒得写博客了。