A*B Problem(FFT模板)

原题链接 ( AcWing版 )

原题链接 ( 洛谷版 )

题目描述

给定两个正整数 A A A 和 B B B,请你计算 A × B A×B A×B 的值。

输入格式

共两行,第一行包含整数 A A A,第二行包含整数 B B B。

输出格式

共一行,包含 A × B A×B A×B 的值。

数据范围

1 ≤ A 1 ≤ A 1≤A 与 B B B 的长度 ≤ 1 0 5 10^5 105。

输入样例:

2
3

输出样例:

6

强烈推荐这个视频:快速傅里叶变换(FFT)——有史以来最巧妙的算法?

#include<bits/stdc++.h>
using namespace std;
const int N=3e6+10;
const double pi=acos(-1.0);
char s1[N],s2[N]; int n,m,res[N],k,p,bit,rev[N];
struct C{
	double x,y;
	C operator+(const C& t){ return {x+t.x,y+t.y}; }
	C operator-(const C& t){ return {x-t.x,y-t.y}; }
	C operator*(const C& t){ return { x*t.x-y*t.y,x*t.y+y*t.x }; }
}a[N],b[N];

void fft(C* a,int n,int inv){	//递归版
	if(n==1)	return;
	C l[n>>1],r[n>>1];
	for(int i=0;i<(n>>1);i++) l[i]=a[i<<1],r[i]=a[i<<1|1];
	fft(l,n>>1,inv);	fft(r,n>>1,inv);
	C wn{cos(2*pi/n),inv*sin(2*pi/n)},w{1,0};
	for(int i=0;i<(n>>1);i++,w=w*wn)
		a[i]=l[i]+w*r[i], a[i+(n>>1)]=l[i]-w*r[i];
}

void fft(C* a,int inv){		//迭代版
	for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
	for(int mid=1;mid<n;mid<<=1){
		C wn={cos(pi/mid),inv*sin(pi/mid)};
		for(int i=0;i<n;i+=mid<<1){
			C w={1,0};
			for(int j=0;j<mid;j++,w=w*wn){
				C x=a[i+j],y=w*a[i+j+mid];
				a[i+j]=x+y,a[i+j+mid]=x-y;
			}
		}
	}
}

int main(){
	scanf("%s%s",s1,s2);
	n=strlen(s1),m=strlen(s2);
	for(int i=0;i<n;i++)	a[i].x=s1[i]-'0';
	for(int i=0;i<m;i++)	b[i].x=s2[i]-'0';
	for(m=n+m,bit=1;(1<<bit)<m;bit++);	n=(1<<bit);
	for(int i=1;i<n;i++)	//rev数组在迭代FFT时需要用到
		rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
	fft(a,1);	fft(b,1);
	for(int i=0;i<n;i++)	a[i]=a[i]*b[i];
	fft(a,-1);
	reverse(a,a+m-1);
	for(p=0;p<m-1||k;p++){
		res[p]=((int)(a[p].x/n+0.5+k))%10;
		k=(a[p].x/n+0.5+k)/10;
	}
	for(p--;p>=0;p--) printf("%d",res[p]);
}

注意

这题数组要稍微开大些。AcWing上两种方法都可以过,洛谷上只有迭代版可以过。
A*B Problem(FFT模板)

代码细节解释

  • r e v rev rev 数组在迭代 F F T FFT FFT 时会用到,具体作用参考下图。画的图有点丑。

A*B Problem(FFT模板)

  • 关于 r e v rev rev 的求法也有递推的思想, r e v [ i ] rev[i] rev[i] 与 r e v [ i > > 1 ] rev[i>>1] rev[i>>1] 有关。

  • 行列式求逆的时候,多出来一个 1 n \frac1n n1​ ,所以最后求得的系数还要再除 n n n。
    A*B Problem(FFT模板)

  • for(int i=0;i<n;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    这行代码中的if(i<rev[i])可以防止重复交换,不可以写为if(i!=rev[i])

  • C x=a[i+j],y=w*a[i+j+mid]; a[i+j]=x+y,a[i+j+mid]=x-y;
    这两行代码也不可以写成:
    a[i+j]=a[i+j]+w*[i+j+mid]; a[i+j+mid]=a[i+j]-w*a[i+j+mid];
    因为会修改 a [ i + j ] a[i+j] a[i+j] ,所以只能把 a [ i + j ] a[i+j] a[i+j] 与 a [ i + j + m i d ] a[i+j+mid] a[i+j+mid] 先保存起来。

上一篇:ceres教程(3)ceres::Problem


下一篇:对抗搜索Problem J. Situation