zoukankan      html  css  js  c++  java
  • 【模板】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;
    }
  • 相关阅读:
    vue表单验证定位到错误的地方
    INSPINIA Version 2.5 Bootstrap3
    volatile 彻底搞懂
    solr6.4.2之webservice兼容升级
    快速排序
    Elasticsearch调优篇 10
    TCP 连接为什么需要 3 次握手和 4 次挥手
    搜索技术与方案的选型与思考
    Elasticsearch调优篇 09
    Elasticsearch调优篇 08
  • 原文地址:https://www.cnblogs.com/lfri/p/11575465.html
Copyright © 2011-2022 走看看