洛谷 P3803 【模板】多项式乘法(FFT)fft

题目描述

给定一个n次多项式F(x),和一个m次多项式G(x)。

请求出F(x)和G(x)的卷积。

输入输出格式

输入格式:
第一行2个正整数n,m。

接下来一行n+1个数字,从低到高表示F(x)的系数。

接下来一行m+1个数字,从低到高表示G(x))的系数。

输出格式:
一行n+m+1个数字,从低到高表示F(x)∗G(x)的系数。

输入输出样例

输入样例#1:
1 2
1 2
1 2 1
输出样例#1:
1 4 5 2
说明

保证输入中的系数大于等于 0 且小于等于9。

对于100%的数据: n,m≤10e6

数据有一定梯度。

空间限制:256MB

分析:搞了差不多两天,终于搞定了,个人感觉最难理解迭代优化。

参考资料:
洛谷dalao的题解
关于迭代优化

代码:

#include <iostream>
#include <cstdio>
#include <cmath>

const int maxn=1e6*4;
const double pi=acos(-1);

using namespace std;

struct rec{
    double x,y;
};

rec operator + (const rec a,const rec b)
{
    return (rec){a.x+b.x,a.y+b.y};
}
rec operator - (rec a,rec b)
{
    return (rec){a.x-b.x,a.y-b.y};
}
rec operator * (rec a,rec b)
{
    return (rec){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
//一堆复数的计算符
rec a[maxn],b[maxn];
int r[maxn];
int n,m,l,p;

void fft(rec *a,int f)
{
    for (int i=0;i<l;i++)//按位置交换
    {
        if (r[i]>i) swap(a[i],a[r[i]]);
    }
    for (int i=2;i<=l;i*=2)//枚举合并的区间长度
    {
        rec wn=(rec){cos(2*pi/i),f*sin(2*pi/i)};//一个单位复根
        for (int j=0;j<l;j+=i)//枚举区间起点
        {
            rec w=(rec){1,0};
            for (int k=0;k<i/2;k++)//枚举区间,只需枚举一半
            {
                rec u=a[j+k],v=w*a[j+k+i/2];
                a[j+k]=u+v;//一个蝴蝶变换
                a[j+k+i/2]=u-v;
                w=w*wn;
            }
        }
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=0;i<=n;i++) scanf("%lf",&a[i].x);
    for (int i=0;i<=m;i++) scanf("%lf",&b[i].x);
    l=1;
    while (l<=m+n)
    {
        l*=2;
        p++;
    }
    for (int i=0;i<l;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(p-1)); 
    fft(a,1);
    fft(b,1);
    for (int i=0;i<l;i++) a[i]=a[i]*b[i];   
    fft(a,-1);  
    for (int i=0;i<=n+m;i++) printf("%.0lf ",a[i].x/l+0.233);
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/79920691