• 拆系数FFT


    拆系数FFT

    表示才发现自己没有掌握这个似乎烂大街了的科技了……

    概念:

    应对那种模数比较恶心人的多项式乘法,大概就是吧一个多项式拆成两个,然后让乘法不会爆掉,最后再进行取模。既然拆成了两个多项式,(DFT)(IDFT)次数自然就会变多,一共有(7)次的和(4)次的两种写法,自然是后面的快一些啦,但是后边的精度要求比较高,并且一般也不会卡的这么严重的,这里就只介绍第一种吧。

    具体实现:

    我们回忆(FFT)的过程,是将多项式转化为点值表示,相乘之后再插值回来。如果模数较好的话,模意义下(NTT)比较好,如果模数不好,但是数值范围较小的时候,(FFT)也是可以的。然而如果没有可以做(NTT)的模数,并且直接(FFT)强上会爆掉的时候,就需要拆系数(FFT)了。
    我们想将多项式(A(x))进行拆分,得到两个新的多项式(B(x), C(x))。其中(A_i = B_i * x^{frac{n}{2}} + C_i),如此处理,让我们直接进行乘法的时候不会爆精。具体过程就是这样,假设我们做(A * B)的卷积,那么拆分后

    [A(x) = C(x) * x^{frac{n}{2}} + D(x),B(x) = E(x) * x^{frac{n}{2}} + F(x) ]

    原来的卷积式变成了

    [(C(x) * x^{frac{n}{2}} + D(x)) * (E(x) * x^{frac{n}{2}} + F(x)) ]

    暴力展开可得

    [C(x) * E(x) * x^n + (C(x) * F(x) + D(x) * E(x)) * x^{frac{n}{2}} + D(x) * F(x) ]

    这样总计(4)(DFT)(3)(IDFT),常数变得超级大……

    (4)次的坑以后填吧……

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 500;
    typedef long long ll;
    const double PI = acos(-1);
    typedef vector<int> Vec;
    int Md;
    
    inline int Add(const int &x, const int &y) { return (x + y >= Md) ? (x + y - Md) : (x + y);}
    inline int Sub(const int &x, const int &y) { return (x - y < 0) ? (x - y + Md) : (x - y);}
    inline int Mul(const int &x, const int &y) { return (ll)x * y % Md;}
    int Powe(int x, int y) {
      int ans = 1;
      while(y) {
        if(y & 1) ans = Mul(ans, x);
        x = Mul(x, x);
        y >>= 1;
      }
      return ans;
    }
    
    namespace Poly {
      struct Complex {
        double x, y;
        void operator = (int a) {
          x = a;
          y = 0;
        }
        friend Complex operator + (Complex A, Complex B) {
          return (Complex) { A.x + B.x, A.y + B.y};
        }
        friend Complex operator - (Complex A, Complex B) {
          return (Complex) { A.x - B.x, A.y - B.y};
        }
        friend Complex operator * (Complex A, Complex B) {
          return (Complex) { A.x * B.x - A.y * B.y, A.x * B.y + A.y * B.x};
        }
        friend Complex operator / (Complex A, int B) {
          return (Complex) { A.x / B, A.y / B};
        }
      } w[N << 2], A[N << 2], B[N << 2], C[N << 2], D[N << 2];
    
      int rev[N << 2 | 1], l = 1;
      
      void Pre(int len) {
        for(; l < len; l <<= 1) {
          for(int i = l; i < (l << 1); i++) {
            w[i] = (Complex) { cos(PI / l * (i - l)), sin(PI / l * (i - l))};
          }
        }
        for(int i = 0; i < len; i++) {
          rev[i] = (rev[i >> 1] >> 1) | ((i & 1) ? len >> 1 : 0);
        }
      }
    
      void DFT(Complex *A, int len) {
        for(int i = 0; i < len; i++) if(i < rev[i]) swap(A[i], A[rev[i]]);
        for(int i = 1; i < len; i <<= 1) {
          for(int j = 0; j < len; j += i << 1) {
            Complex x, y;
            for(int k = 0; k < i; k++) {
              x = A[j + k], y = A[i + j + k] * w[i + k];
              A[j + k] = x + y, A[i + j + k] = x - y;
            }
          }
        }
      }
    
      void IDFT(Complex *A, int len) {
        reverse(A + 1, A + len);
        DFT(A, len);
        for(int i = 0; i < len; i++) A[i] = A[i] / len;
      }
    
      Vec MUL(Vec a, Vec b) {
        int n = a.size(), m = b.size(), len;
        for(len = 1; len < n + m - 1; len <<= 1);
        a.resize(len), b.resize(len);
        Pre(len);
        for(int i = 0; i < len; i++) {
          A[i] = a[i] >> 15;
          B[i] = a[i] & 32767;
          C[i] = b[i] >> 15;
          D[i] = b[i] & 32767;
        }
        DFT(A, len), DFT(B, len), DFT(C, len), DFT(D, len);
        for(int i = 0; i < len; i++) {
          Complex AA = A[i] * C[i], BB = A[i] * D[i] + B[i] * C[i], CC = B[i] * D[i];
          A[i] = AA; B[i] = BB; C[i] = CC;
        }
        IDFT(A, len); IDFT(B, len); IDFT(C, len);
        for(int i = 0; i < len; i++) {
          ll x = llround(A[i].x) % Md, y =llround(B[i].x) % Md, z = llround(C[i].x) % Md;
          a[i] = ((x << 30) % Md + (y << 15) % Md + z ) % Md;
        }
        a.resize(n + m - 1);
        return a;
      }
    }
    
    
    int n, m;
    
    int main() {
      scanf("%d%d%d", &n, &m, &Md);
      Vec a(n + 1), b(m + 1);
      for(int i = 0; i <= n; i++) scanf("%d", &a[i]);
      for(int i = 0; i <= m; i++) scanf("%d", &b[i]);
      a = Poly::MUL(a, b);
      for(int i = 0; i < (int)a.size(); i++) printf("%d ", a[i]);
      puts("");
      return 0;
    }
    
  • 相关阅读:
    树莓派4B对接苹果Homebrigde
    分享一款好看的PyCharm风格(转)
    Centos7安装opencv-python缺少共享库(libSM.so.6, libXrender.so.1, libXext.so.6)的解决办法
    win10 python3.7安装 opencv 和 face_recognition
    Python出现Could not find a version that satisfies the requirement openpyxl (from versions: )
    从零开始开发标准的s57电子海图第十三篇 电子海图中特征记录各字段结构和内容(共一百篇)
    从零开始开发标准的s57电子海图第十二篇 编码原则与记录结构(共一百篇)
    从零开始开发标准的s57电子海图第十一篇--海图文件中的数据类型(共一百篇)
    从零开始开发标准的s57电子海图第十篇--海图文件示例(共一百篇)
    从零开始开发标准的s57电子海图第九篇--数据记录字段DR区的结构(共一百篇)
  • 原文地址:https://www.cnblogs.com/Apocrypha/p/10507645.html
Copyright © 2020-2023  润新知