多项式乘法

https://www.luogu.com.cn/problem/P3803

题意:给出两个多项式,求两个多项式的卷积。

1解法:FFT递归版,因为递归每次传数组导致慢。

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
#define mod 20191117
#define PI acos(-1)
using namespace std;
typedef long long ll ;
const int MAXN = 2*1e6+10;
const double pi =acos(-1.0);

struct complex{
    double x , y ;
    complex(double xx = 0 , double yy = 0){
        x = xx , y = yy ;
    }
}a[MAXN],b[MAXN];

complex operator + (complex a , complex b)
{
    return complex(a.x+b.x , a.y+b.y);
}
complex operator - (complex a , complex b)
{
    return complex(a.x-b.x , a.y-b.y);
}
complex operator * (complex a , complex b)
{
    return complex(a.x*b.x - a.y*b.y , a.x*b.y+a.y*b.x);
}

void fast_fast_tle(int limit , complex *a , int type)
{
    if(limit == 1) return ;//只有一个常数项
    complex a1[limit>>1] , a2[limit>>1];
    for(int i = 0 ; i <= limit ; i+=2)
        a1[i>>1] = a[i] , a2[i>>1] = a[i+1];//下标分奇偶存入两数组
    fast_fast_tle(limit>>1 , a1 , type);//分治
    fast_fast_tle(limit>>1 , a2 , type);
    complex Wn = complex(cos(2.0*pi/limit) , type*sin(2.0*pi/limit)), w = complex(1 , 0);//单位根及幂
    for(int i = 0 ; i < (limit>>1) ; i++ , w = w * Wn)
    {
        a[i] = a1[i] + w*a2[i];
        a[i+(limit>>1)] = a1[i] - w*a2[i];//利用单位根的性质,得到另一部分
    }
}

int main()
{
    int n , m;
    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);
    int limit = 1 ; while(limit <= n+m) limit <<= 1;
    fast_fast_tle(limit , a , 1);
    fast_fast_tle(limit , b , 1);
    //1表示从系数表示法变为点值表示
    for(int i = 0 ; i <= limit ; i++)
        a[i] = a[i] * b[i];//点值表示法的乘积O(n)
    fast_fast_tle(limit , a , -1);
    //傅里叶逆变换,-1表示点值表示法变为系数表示法
    for(int i = 0 ; i <= n+m ; i++) cout << (int)(a[i].x/limit+0.5) << " ";

    return 0;
}

 2解法:FFT迭代版,因为我们需要求的序列实际是原序列下标的二进制反转!

这样我们可以O(n)的利用某种操作得到我们要求的序列,然后不断向上合并就好了.

//#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <string.h>
#include <vector>
#define ME(x , y) memset(x , y , sizeof(x))
#define SF(n) scanf("%d" , &n)
#define rep(i , n) for(int i = 0 ; i < n ; i ++)
#define INF  0x3f3f3f3f
#define mod 20191117
#define PI acos(-1)
using namespace std;
typedef long long ll ;
const int MAXN = 3e6+9;
const double pi =acos(-1.0);
int r[MAXN] , limit = 1 , l = 0;

struct complex{
    double x , y ;
    complex(double xx = 0 , double yy = 0){
        x = xx , y = yy ;
    }
}a[MAXN],b[MAXN];

complex operator + (complex a , complex b)
{
    return complex(a.x+b.x , a.y+b.y);
}
complex operator - (complex a , complex b)
{
    return complex(a.x-b.x , a.y-b.y);
}
complex operator * (complex a , complex b)
{
    return complex(a.x*b.x - a.y*b.y , a.x*b.y+a.y*b.x);
}

void fast_fast_tle(complex *a , int type)
{
    for(int i = 0 ; i < limit ; i++)
        if(i < r[i]) swap(a[i] , a[r[i]]);//将序列下标变成迭代序列
    for(int mid = 1 ; mid < limit ; mid<<=1)//待合并的中点
    {
        complex Wn(cos(pi/mid) , type*sin(pi/mid));//单位根
        for(int R = mid<<1 , j = 0 ; j < limit ; j+=R)//R是区间右端点,j表示目前位置
        {
            complex w(1,0);
            for(int k = 0 ; k < mid ; k++ , w = w * Wn)//枚举左半部分
            {
                complex x = a[j+k] , y = w * a[j+mid+k];//蝴蝶效应
                a[j+k] = x + y ;
                a[j+mid+k] = x - y ;
            }
        }
    }
}

int main()
{
    int n , m ;
    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);
    while(limit <= n+m) limit <<= 1 , l++;
    for(int i = 0 ; i < limit ; i++)
    {
        r[i] = (r[i>>1]>>1) | ((i&1) << (l-1));//将标号二进制数反过来,存入r[i]数组中
    }
    fast_fast_tle(a , 1);
    fast_fast_tle(b , 1);
    for(int i = 0 ; i <= limit ; i++) a[i] = a[i]*b[i];
    fast_fast_tle(a , -1);
    for(int i = 0 ; i <= n+m ; i++)
        printf("%d " , (int)(a[i].x/limit + 0.5));


    return 0;
}

 

posted @ 2020-02-02 19:38  无名菜鸟1  阅读(256)  评论(0编辑  收藏  举报