HDU1402(FFT入门)

题目链接:http://acm.hdu.edu.cn/status.php?user=Reykjavik11207&pid=1402&status=5

本题数据范围为5e4,常规方法O(n2)肯定是不行的。

FFT是离散傅里叶变换DFT的快速形式

对多项式f(x) = a0 + a1x + a2x2 + ··· +an-1xn-1,有两种表示法:

系数表达式 : (a0 , a1 , ··· , an-1)

由于n-1次多项式需要n个点来确定

所以可以用点值表达式 : ( (x0,f(x0)) , (x1,f(x1)) , ··· , (xn-1,f(xn-1)) ) 来表示

要获得点值表达式,首先选取n个x值获得对应f(x)的值

将f(x)分为奇偶两个部分f(x) = a0 + a2x2 + ··· + an-2xn-2 + a1x + a3x3 + ··· + an-1xn-1

f1(x) = a0 + a2x + ··· + an-2 x(n-2)/2

   f2(x) = a1 + a3x + ··· + an-1 x(n-1)/2

则有f(x) = f1(x2) + xf2(x2

f1(x)与f2(x)再分别分解,直至到常数ai为止

到了这里,复杂度并没有降低,反而由于x的整次幂未知还升高了,可以发现x = 1可以使式子更简单,因为1的多少次幂都是1,然后就是-1,但只有2个数远远不够,所以引入了复数。

 

是复平面单位圆上逆时针按k从小到大均匀分布的复根,间隔角度为2π/n,所以有:

= cos(2kπ/n) + i * sin(2kπ/n) ,计算复根的k次幂显然较实数更为方便,(但STL中三角函数也不是O(1),是多少我也不太懂,总之我把wnk函数放三重循环里就超时了)。

容易看出它的周期为n,即满足

同时还有以下性质:  =  cos(2kπ/n + π) + i * sin(2kπ/n + π) =

=  cos(2*2k*π/n) + i * sin(2*2k*π/n) = cos(2kπ/(n/2)) + i * sin(2kπ/(n/2)) =

故f(wnk+n/2) = f1(wn/2k) - wnk f2(wn/2k)

以n = 4为例:

显然n必须为2的整数次幂

原来第i个元素的位置经过变换后的位置为i的二进制按长度翻转,

如上图中(0)10 = (00)2   翻转 ->   (00)2 = (0)10

    (1)10 = (01)2   翻转 ->   (10)2 = (2)10

    (2)10 = (10)2   翻转->    (01)2 = (1)10

    (3)10 = (11)2   翻转->    (11)2 = (3)10

然后自底向上代入函数中,得到n个f(x)值

另一个数同理得到n个g(x)值

F(x) = f(x)*g(x),则F(xi) = f(xi)*g(xi)

接下来就要用傅里叶逆变换IDFT求出F(x)的系数表达式

变换矩阵

的逆矩阵为

进而得到F(x)的系数表达式,即为结果

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1 << 17;
const double pi = acos(-1.0);
const double eps = 1e-4;//这个...精度问题,本来用的1e-8就WA了
int n,len;
int rev(int x)//二进制翻转
{
    int res = 0;
    for(int i = 0; i < len; i++)
        res += ((x >> i) & 1) << (len - 1 - i);
    return res;
}
struct Complex
{
    double real,image;
    Complex(double r = 0,double i = 0)
    {
        real = r;
        image = i;
    }
    Complex operator + (const Complex &t)
    {
        return Complex(real + t.real, image + t.image);
    }
    Complex operator - (const Complex &t)
    {
        return Complex(real - t.real, image - t.image);
    }
    Complex operator * (const Complex &t)
    {
        return Complex(real * t.real - image * t.image, real * t.image + t.real * image);
    }
};
Complex wnk(double n,double k)
{
    return Complex(cos(2*pi*k/n), sin(2*pi*k/n));
}
void fft(Complex y[], int dft)
{
    Complex t1,t2;
    for(int i = 1; i < n; i <<= 1)
    {
        Complex W(1,0), w = wnk(2*i,dft);
        for(int k = 0; k < i; k++)
        {
            for(int j = k; j < n; j += i<<1)
            {
                t1 = y[j] + W * y[j+i];
                t2 = y[j] - W * y[j+i];
                y[j] = t1;
                y[j+i] = t2;
            }
            W = W * w;
        }
    }
    if(dft == -1)
    {
        for(int i = 0; i < n; i++)
            y[i].real /= n;
    }
}
Complex a1[N],a2[N],a[N];
int ans[N];
char stra[N>>1],strb[N>>1];
int main()
{
    while(~scanf("%s %s",stra,strb))
    {
        int lena = strlen(stra);
        int lenb = strlen(strb);
        len = log10(lena+lenb)/log10(2) + 1 + eps;
        n = 1 << len;
        for(int i = 0; i < lena; i++)
            a1[rev(i)] = Complex((double)(stra[lena-i-1] - '0'), 0);
        for(int i = lena; i < n; i++)
            a1[rev(i)] = Complex(0,0);
        for(int i = 0; i < lenb; i++)
            a2[rev(i)] = Complex((double)(strb[lenb-i-1] - '0'), 0);
        for(int i = lenb; i < n; i++)
            a2[rev(i)] = Complex(0,0);
        fft(a1,1);
        fft(a2,1);
        for(int i = 0; i < n; i++)
            a[rev(i)] = a1[i] * a2[i];
        fft(a,-1);
        int t = 0;
        for(int i = 0; i < n; i++)
        {
            ans[n - 1 - i] = ((int)(a[i].real + eps) + t) % 10;
            t = ((int)(a[i].real + eps) + t) / 10;
        }
        bool flag = 0;
        for(int i = 0; i < n - 1; i++)
        {
            if(ans[i])
                flag = 1;
            if(!flag)
                continue;
            printf("%d",ans[i]);
        }
        printf("%d\n",ans[n-1]);
    }
    return 0;
}

 

(W0n)0(W1n)0(Wn1n)0(W0n)1(W1n)1(Wn1n)1⋯⋱⋯(W0n)n−1(W1n)n−1⋮(Wn−1n)n−1⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢a0a1⋮an−1⎤⎦⎥⎥⎥⎥

(W0n)0(W1n)0(Wn1n)0(W0n)1(W1n)1(Wn1n)1⋯⋱⋯(W0n)n−1(W1n)n−1⋮(Wn−1n)n−1⎤⎦⎥⎥⎥⎥⎥⎡⎣⎢⎢⎢⎢a0a1⋮an−1⎤⎦⎥⎥⎥⎥

转载于:https://www.cnblogs.com/westwind1005/p/8886973.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值