您的位置:首页 > 其它

HDU 1402 fft 模板题

2015-10-26 12:19 501 查看
题目就是求一个大数的乘法

这里数字的位数有50000的长度,按平时的乘法方式计算,每一位相乘是要n^2的复杂度的,这肯定不行

我们可以将每一位分解后作为系数,如153 = 1*x^2 + 5*x^1 + 3*x^0 (在这里x可以理解成10)

那么两个数字相乘就相当于系数相乘后得到新的系数组合

如153 * 4987 = <3,5,1> * <7,8,9,4>

这相当于卷积的计算,最快的方式就是fft,nlgn的复杂度就能求解,求解得到后再把每一位大于10往前赋值就行了

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>

using namespace std;
const double PI = acos(-1.0);

struct complex{
double r , i;
complex(double r=0 , double i=0):r(r),i(i){}
complex operator+(const complex &a) const{
return complex(r+a.r , i+a.i);
}
complex operator-(const complex &a) const{
return complex(r-a.r , i-a.i);
}
complex operator*(const complex &a) const{
return complex(r*a.r-i*a.i , r*a.i+i*a.r);
}
};

void change(complex y[] , int len)
{
int i,j,k;
for(i=1 , j=len/2 ; i<len-1 ; i++){
if(i<j) swap(y[i],y[j]);
k = len/2;
while(j>=k){
j-=k;
k/=2;
}
if(j<k) j+=k;
}
}

void fft(complex y[] , int len , int on)
{
change(y , len);
for(int i=2 ; i<=len ; i<<=1){
complex wn(cos(-on*2*PI/i) , sin(-on*2*PI/i));
for(int j=0 ; j<len ; j+=i){
complex w(1,0);
for(int k=j ; k<j+i/2 ; k++){
complex u = y[k];
complex t = w*y[k+i/2];
y[k] = u+t;
y[k+i/2] = u-t;
w = w*wn;
}
}
}
if(on==-1)
for(int i=0 ; i<len ; i++)
y[i].r /= len;

}

const int MAXN = 200010;
complex x1[MAXN] , x2[MAXN];
char str1[MAXN] , str2[MAXN];
int sum[MAXN];

int main()
{
while(~scanf("%s%s" , str1 , str2)){
int len1 = strlen(str1) , len2 = strlen(str2);
int len = 1;
//fft的计算,由于是倍增的,需要保证是2^k次方,且要大于最后得到的结果的总位数
while(len<len1*2 || len<len2*2) len<<=1;
for(int i=0 ; i<len1 ; i++)
x1[i] = complex(str1[len1-1-i]-'0' , 0);
for(int i=len1 ; i<len ; i++)
x1[i] = complex(0 , 0);
for(int i=0 ; i<len2 ; i++)
x2[i] = complex(str2[len2-1-i]-'0' , 0);
for(int i=len2 ; i<len ; i++)
x2[i] = complex(0 , 0);
//将当前的组合数的系数值修改成复数坐标系的点值o(nlgn)
fft(x1 , len , 1);
fft(x2 , len , 1);
//点值可以o(n)的时间内进行计算
for(int i=0 ; i<len ; i++)
x1[i] = x1[i]*x2[i];
//将点值重新通过逆过程(又叫dft)转化为系数值
fft(x1 , len , -1);

memset(sum , 0 , sizeof(sum));
for(int i=0 ; i<len ; i++) sum[i] = (int)(x1[i].r+0.5);
for(int i=0 ; i<len ; i++){
sum[i+1] += sum[i]/10;
sum[i] = sum[i]%10;
}
int ansl = len1+len2-1;
while(sum[ansl]==0 && ansl>0) ansl--;
for(int i=ansl ; i>=0 ; i--) printf("%c" , sum[i]+'0');
printf("\n");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: