HDU 1402 A * B Problem Plus(快速傅里叶变换)

题意:两个数相乘,每个数的长度不超过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)