FFT的一个小技巧

版权声明:转载注明出处,谢谢,有问题可以向博主联系 https://blog.csdn.net/VictoryCzt/article/details/84675238

蒟蒻的FFT学习笔记IN

对于一般的,我们将两个多项式相乘,都是开两个虚数数组, A , B A,B ,先将每个多项式的每个系数分别存入两个数组的实部,然后先将 A A 做FFT变换,再将 B B 做FFT变换,然后两个点值表达式相乘,然后再将其 F F T FFT 变换回来,最后答案就是数组的实部。

这样是做了3次 n l o g n nlogn 的FFT变换。


但是有一个小技巧,我们对于两个多项式,其实只用开一个虚数数组,将第一个多项式 的系数存入这个数组的实部,将第二个多项式的系数存入这个数组的虚部,然后先对其进行FFT变换,然后自己乘自己后再逆变换回来,虚部除以 2 n 2n 便是最后的答案。

此时,我们可以发现,只做了2次 n l o g n nlogn 的FFT变换。

目前原理待博主研究后再写上。

上份代码:
模板

Luogu FFT 模板题
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define db double
using namespace std;
const int M=3e6+10;
const db pi=acos(-1);
struct complex{
	db x,y;
	complex(){}
	complex(db a,db b):x(a),y(b){}
}A[M];
complex operator +(const complex &a,complex &b){return complex(a.x+b.x,a.y+b.y);}
complex operator -(const complex &a,complex &b){return complex(a.x-b.x,a.y-b.y);}
complex operator *(const complex &a,complex &b){return complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);}
int R[M];
void DFT(complex *a,int n,int f){
	for(int i=0;i<=n;i++)if(i<R[i])swap(a[i],a[R[i]]);
	for(int i=2;i<=n;i<<=1){
		int now=i>>1;
		complex wn=complex(cos(2*pi/i),f*sin(2*pi/i));
		for(int j=0;j<n;j+=i){
			complex w=complex(1,0),x,y;
			for(int k=j;k<j+now;k++,w=w*wn){
				x=a[k],y=w*a[k+now];
				a[k]=x+y;a[k+now]=x-y;
			}
		}
	}
	if(f==-1)for(int i=0;i<=n;i++)a[i].y/=(2*n);
}
void FFT(complex *a,int n,int m){
	int K,lg;
	for(K=1,lg=0;K<=n+m;K<<=1,++lg);--lg;
	for(int i=0;i<=K;i++)R[i]=(R[i>>1]>>1)|((i&1)<<lg);
	DFT(a,K,1);
	for(int i=0;i<=K;i++)a[i]=a[i]*a[i];
	DFT(a,K,-1);
}
int n,m,x;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n;i++)scanf("%d",&x),A[i].x=x;
	for(int i=0;i<=m;i++)scanf("%d",&x),A[i].y=x;
	FFT(A,n,m);
	for(int i=0;i<=n+m;i++){
		printf("%d ",int(A[i].y+0.5));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/VictoryCzt/article/details/84675238