【bzoj2179】FFT快速傅里叶变换(优化高精度乘法)

#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1)
typedef complex<double> C;
const int N=201100;
int n,m,l,r[N],ans[N];
C a[N],b[N];
char s[N],t[N];
void fft(C *a,int f){
    for(int i=0;i<n;++i) if(r[i]>i) swap(a[i],a[r[i]]);//分配正确的顺序
    for(int i=1;i<n;i<<=1){//此时i枚举的是当前层小区间的长度 
        C wn(cos(pi/i),f*sin(pi/i));//由于需要大区间的单位根,所以2*pi/2*i,消去2
        for(int j=0;j<n;j+=i<<1){ //j是你合并形成的每个区间的左端点 
            C w=1;
            for(int k=0;k<i;k++,w*=wn){//在小区间内枚举复数,一次合并两个小区间
                C x=a[j+k],y=w*a[j+k+i];//蝴蝶变换
                a[j+k]=x+y;a[j+k+i]=x-y;
            }
        }
    }
}
int main(){
    scanf("%d%s%s",&m,s,t);
    for(int i=0;i<m;++i) a[i]=s[m-i-1]-'0';
    for(int i=0;i<m;++i) b[i]=t[m-i-1]-'0';
    for(n=1,m<<=1;n<m;n<<=1) l++;
    for(int i=0;i<n;++i) r[i]=((r[i>>1]>>1)|((i&1)<<(l-1)));//i到i*2要<<1,则r[i]到r[i*2]就要>>1,由于是从r[i]推到r[i*2],i*2第最低位的信息会缺失,所以要|((i&1)<<(l-1))
    fft(a,1),fft(b,1);//通过离散傅里叶变换(把n个复数带入多项式,得到点值表示)求出a,b的点值表示
    for(int i=0;i<n;++i) a[i]*=b[i];//新的多项式的点至表示就是g(x){(x0,a(x0)*b(x0)));(x1,a(x1)*b(x1));...(xn-1,a(xn-1)*b(xn-1))}
    fft(a,-1);//把前面的点值表示作为新多项式的系数,并把新多项式转化成系数表示法
    for(int i=0;i<n;++i) a[i]/=n;
    for(int i=0;i<m;++i) ans[i]=(int)(a[i].real()+0.1);
    for(int i=0;i<m;++i){
        if(ans[i]>=10){
            ans[i+1]+=ans[i]/10;ans[i]%=10;
        }else if(!ans[i]&&i==m-1) m--;
    }
    while(!ans[m-1]&&m>0) m--;
    for(int i=m-1;i>=0;--i) printf("%d",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kgxw0430/p/10296154.html