标签:
题意:两个数相乘,每个数的长度不超过10^5;
思路:FFT第一题。通过将系数表达式转换为点值表达式,降低复杂度;算导是个好东西!!!
用DFT实现单位复根计算点值表达式,逆DFT则将点值表达式转为系数表达式,即计算插值;复杂度均为O(n^2);
FFT采用分治的思想,将奇偶分开处理,优化DFT;复杂度为O(nlogn);
FFT处理时,(将点值扩展到2^n)->(计算点值)->(点乘运算)->(计算插值);
由于奇偶分治时结果的位置发生变化,需要在傅里叶变换时进行二进制位置互换;
每经过一次蝴蝶操作DFT次数乘二,则控制层数;
每次迭代使w的值不断变化可以节约每次通过for循环从0开始计算wn的时间;蝴蝶操作计算点值;
DFT的逆计算插值;
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define pi acos(-1.0) char str1[5000100],str2[5000100]; int num[5000100]; int len1,len2,n,m; struct Complex{ double r,i; Complex(){}; Complex(double x,double y){ r=x;i=y; } Complex operator +(const Complex &t)const{ return Complex(r+t.r,i+t.i); } Complex operator -(const Complex &t)const{ return Complex(r-t.r,i-t.i); } Complex operator *(const Complex &t)const{ return Complex(r*t.r-i*t.i,r*t.i+i*t.r); } }x1[5000100],x2[5000100]; void fft(Complex y[],int n,int rev) { for(int i=1,j,k,t;i<n;i++){ for(j=0,k=n>>1,t=i;k;k>>=1,t>>=1) j=j<<1|t&1; //二进制位置互换 if(i<j) swap(y[i],y[j]); } for(int s=2,ds=1;s<=n;ds=s,s<<=1){ //控制层数 Complex wn=Complex(cos(rev*2*pi/s),sin(rev*2*pi/s)),w=Complex(1,0),t; //初始化单位复根和螺旋因子 for(int k=0;k<ds;k++,w=w*wn){ //更新螺旋因子 for(int i=k;i<n;i+=s){ t=w*y[i+ds]; //蝴蝶操作 y[i+ds]=y[i]-t; y[i]=y[i]+t; } } } if(rev==-1) for(int i=0;i<n;i++) y[i].r/=n; //求逆 } int main() { int i,j,k,t; while(scanf("%s%s",str1,str2)!=EOF) { len1=strlen(str1); len2=strlen(str2); n=1; while(n<len1+len2) n<<=1; for(i=0;i<len1;i++) x1[i]=Complex(str1[len1-i-1]-‘0‘,0); for(;i<n;i++) x1[i]=Complex(0,0); for(i=0;i<len2;i++) x2[i]=Complex(str2[len2-i-1]-‘0‘,0); for(;i<n;i++) x2[i]=Complex(0,0); //长度扩展 fft(x1,n,1); fft(x2,n,1); //系数表达式转点值表达式 for(i=0;i<n;i++) x1[i]=x1[i]*x2[i]; //点值运算 fft(x1,n,-1); //求插值 for(i=0,t=0;i<n;i++,t/=10){ //结果处理 t+=(int)(x1[i].r+0.1); num[i]=t%10; } for(;t;t/=10) num[i]=t%10; while(n>1&&!num[n-1]) n--; for(i=n-1;i>=0;i--) printf("%d",num[i]); printf("\n"); } return 0; }
HDU 1402 A * B Problem Plus(快速傅里叶变换)
标签:
原文地址:http://www.cnblogs.com/dominating/p/4661273.html