FFT - A * B Problem Plus - HDU 1402

FFT - A * B Problem Plus - HDU 1402

Problem Description

Calculate A * B.

Input

Each line will contain two integers A and B. Process to end of file.

Note: the length of each integer will not exceed 50000.

Output

For each case, output A * B in one line.

Sample Input

1
2
1000
2

Sample Output

2
2000

分析:

F F T 介 绍 可 参 考 刘 汝 佳 白 书 以 及 O I   W I K I 。 FFT介绍可参考刘汝佳白书以及OI\ WIKI。 FFTOI WIKI

简单总结一些规律和性质:

① 、 如 何 理 解 卷 积 : 多 项 式 乘 法 实 际 上 是 多 项 式 系 数 向 量 的 卷 积 。 ①、如何理解卷积:多项式乘法实际上是多项式系数向量的卷积。

② 、 F F T 可 以 加 速 多 项 式 在 系 数 表 示 和 点 值 表 示 之 间 的 转 换 。   多 项 式 的 系 数 表 示 法 对 应 于 时 域 , 而 点 值 表 示 法 对 应 于 频 域 。 ②、FFT可以加速多项式在系数表示和点值表示之间的转换。\\ \ \\\qquad多项式的系数表示法对应于时域,而点值表示法对应于频域。 FFT 

③ 、 点 值 表 示 非 常 适 合 做 乘 法 : 只 要 两 个 多 项 式 的 点 集 ③、点值表示非常适合做乘法:只要两个多项式的点集 { x i x_i xi} 相 同 , 则 只 需 要 把 对 应 的 值 相 乘 即 可 。 相同,则只需要把对应的值相乘即可。

比 如 , C ( x ) = A ( x ) ⋅ B ( x ) , 则 C ( x i ) = A ( x i ) ⋅ B ( x i ) \qquad 比如,C(x)=A(x)·B(x),则C(x_i)=A(x_i)·B(x_i) C(x)=A(x)B(x)C(xi)=A(xi)B(xi)

FFT具体步骤:

① 、 补 0 : 在 两 个 多 项 式 的 最 前 面 补 0 , 补 到 两 个 多 项 式 的 最 高 次 项 为 2 n 次 , n = m a x ( n 1 , n 2 ) 。   设 系 数 向 量 分 别 为 v 1 和 v 2 。 ①、补0:在两个多项式的最前面补0,补到两个多项式的最高次项为2n次,n=max(n1,n2)。\\ \ \\ \qquad 设系数向量分别为v_1和v_2。 002nn=max(n1,n2) v1v2

② 、 求 值 : 用 F F T 计 算 f 1 = D F T ( v 1 ) , f 2 = D F T ( v 2 ) 。 f 1 和 f 2 分 别 是 两 个 多 项 式 在 各 单 位 根 处 的 取 值 。 ②、求值:用FFT计算f_1=DFT(v_1),f_2=DFT(v_2)。f_1和f_2分别是两个多项式在各单位根处的取值。 FFTf1=DFT(v1)f2=DFT(v2)f1f2

③ 、 乘 法 : 把 两 个 向 量 f 1 和 f 2 的 每 一 维 相 乘 , 得 到 向 量 f , 它 对 应 输 入 多 项 式 乘 积 的 点 值 表 示 。 ③、乘法:把两个向量f_1和f_2的每一维相乘,得到向量f,它对应输入多项式乘积的点值表示。 f1f2f

④ 、 插 值 : 用 F F T 计 算 v = I D F T ( f ) , v 就 是 乘 积 的 系 数 向 量 。 ④、插值:用FFT计算v=IDFT(f),v就是乘积的系数向量。 FFTv=IDFT(f)v

代 码 是 k u a n g 神 的 模 板 。 代码是kuang神的模板。 kuang

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>

using namespace std;

const double pi = acos(-1.0);

struct Complex
{
    double x, y;
    Complex(double _x = 0.0, double _y = 0.0)
    {
        x = _x;
        y = _y;
    }
    Complex operator - (const Complex &b) const
    {
        return Complex(x - b.x, y - b.y);
    }
    Complex operator + (const Complex &b) const
    {
        return Complex(x + b.x, y + b.y);
    }
    Complex operator * (const Complex &b) const
    {
        return Complex(x*b.x - y*b.y, x*b.y + y*b.x);
    }
};

void change(Complex y[], int len)
{
    int i, j, k;
    for(int 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;
    }
}
/*
 * 做fft
 * len必须为2^k形式,
 * on==1时是DFT,on==-1时是IDFT
 */
void fft(Complex y[], int len, int on)
{
    change(y, len);
    for(int h = 2; h<=len; h<<=1)
    {
        Complex wn(cos(-on*2*pi/h), sin(-on*2*pi/h));
        for(int j=0; j<len; j+=h)
        {
            Complex w(1,0);
            for(int k=j; k<j+h/2; k++)
            {
                Complex u = y[k];
                Complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if(on == -1)
        for(int i=0;i<len;i++)
            y[i].x /= len;
}

const int N = 2e5+10;

Complex x1[N], x2[N];
char str1[N], str2[N];
int sum[N];

int main()
{
    while(~scanf("%s%s",str1,str2))
    {
        int len1 = strlen(str1), len2 = strlen(str2);
        int len = 1;
        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);
        
        fft(x1, len, 1);		//系数表示转点值表示
        fft(x2, len, 1);
        for(int i=0;i<len;i++)
            x1[i] = x1[i]*x2[i];
        fft(x1, len, -1);		//点值表示转系数表示
        for(int i=0;i<len;i++)  //乘积四舍五入
            sum[i] = (int)(x1[i].x+0.5);
        for(int i=0;i<len;i++)  //处理进位
        {
            sum[i+1] += sum[i]/10;
            sum[i] %= 10;
        }
        len = len1+len2-1;
        while(sum[len]<=0 && len>0) len--;  //前导0
        for(int i=len;i>=0;i--)             //倒序输出
            printf("%c",sum[i]+'0');
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值