【模板】A*B Problem(FFT快速傅里叶)

题目:给出两个n位10进制整数x和y,你需要计算x*y。($n leq 60000$)

分析:

两个正整数的相乘可以视为两个多项式的相乘,

例如 $15 imes 16 = 240$,

可写成 $(5+x)*(6+x) = 30 + 11x + x^2$,$x=10$

这样得到多项式 $A(x)$ 和 $B(x)$,并且能用FFT求出 $C(x)=A(x)B(x)$,

怎么得到最终结果,我们要将 $x=10$ 代入吗?

$n$ 这么大,遍历一遍也没有这么大的数据类型能存下,其次,这也不是必要的。

$x=10$ 是 $C(x)$ 已经相当于十进制,我们模拟一下进位就可以了。

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN = 4 * 60000 + 10;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while (c < '0' || c > '9') {if (c == '-')f = -1; c = getchar();}
    while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
    return x * f;
}
const double Pi = acos(-1.0);
const double Eps = 1e-8;
double ccos[MAXN], ssin[MAXN];
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(ccos[limit] , type * ssin[limit]), w = complex(1, 0);
    //complex Wn = complex(cos(2.0 * Pi / limit) , type * sin(2.0 * Pi / limit)), w = complex(1, 0);
    //Wn为单位根,w表示幂
    for (int i = 0; i < (limit >> 1); i++, w = w * Wn) //这里的w相当于公式中的k
    {
        complex tmp = w * a2[i]; 
        a[i] = a1[i] + tmp;
        a[i + (limit >> 1)] = a1[i] - tmp; //利用单位根的性质,O(1)得到另一部分
    }
}

char s[MAXN];
int res[MAXN];

int main() {
    int N = read();
    scanf("%s", s);
    for (int i = 0; i < N; i++) a[i].x = s[N-1-i]-'0';
    scanf("%s", s);
    for (int i = 0; i < N; i++) b[i].x = s[N-1-i]-'0';

    //for(int i = 0;i < N;i++)  printf("%f ", a[i]);

    int limit = 1; while (limit <= 2*N) limit <<= 1;

    for(int i = 1;i <= limit;i++)
    {
        ccos[i] = cos(2.0 * Pi / i);
        ssin[i] = sin(2.0 * Pi / i);
    }

    fast_fast_tle(limit, a, 1);
    fast_fast_tle(limit, b, 1);
    //后面的1表示要进行的变换是什么类型
    //1表示从系数变为点值
    //-1表示从点值变为系数
    //至于为什么这样是对的,可以参考一下c向量的推导过程,
    for (int i = 0; i <= limit; i++)
        a[i] = a[i] * b[i];
    fast_fast_tle(limit, a, -1);

    for(int i = 0;i <= 2*N;i++)  res[i] = int(a[i].x/limit+0.5);

    int tmp = 0;    //进位
    for(int i = 0;i <= 2*N;i++)
    {
        res[i] += tmp;
        tmp = res[i] / 10;
        res[i] = res[i] % 10;
    }

    bool flag = false;
    for (int i = 2*N; i >= 0; i--)
    {
       if(res[i])  flag = true;  //注意处理前导0,题干有说
        if(flag)  printf("%d", res[i]);  //按照我们推倒的公式,这里还要除以n
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lfri/p/11575465.html