快速数论变换


(本文转载自 桃酱的算法笔记 ,原文戳 链接 ,已获得作者授权)

简介

NTT 解决的是多项式乘法带模数的情况,可以说有些受模数的限制,数也比较大,

但是它比较方便呀毕竟没有复数部分

学习 NTT 之前……

生成子群

子群:群 ,满足 ,则 的子群

拉格朗日定理: 证明需要用到陪集,得到陪集大小等于子群大小,每个陪集要么不相交要么相等,所有陪集的并是集合 ,那么显然成立。

生成子群: 的生成子群 的生成元

阶:群 的阶是满足 的最小的 ,符号 ,有 ,显然成立。

考虑群

阶就是满足 的最小的

原根

满足 ,对于质数 ,也就是说 结果互不相同。

有原根的充要条件 :

离散对数:

因为 是原根,所以 是一个周期,可以取到 的所有元素 对于 是质数时,就是得到 的所有数,就是 的映射 离散对数满足对数的相关性质,如

求原根可以证明满足 的最小的 一定是 的约数 对于质数 ,质因子分解 ,若 恒成立, 的原根

NTT

对于质数 , 原根 满足 , 将 看做 的等价,择其满足相似的性质,比如

然后因为这里涉及到数论变化,所以这里的 (为了区分 FFT 中的 ,我们把这里的 称为 )可以比 FFT 中的 大,但是只要把 看做这里的 就行了,能够避免大小问题……

常见的有

就是 的等价

迭代到长度 ,或者

接下来放一个大数相乘的模板

参考网址如下 https://blog.csdn.net/blackjack_/article/details/79346433

#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#include <vector>
using namespace std;

inline int read() {
  int x = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch <= '9' && ch >= '0') {
    x = 10 * x + ch - '0';
    ch = getchar();
  }
  return x * f;
}
void print(int x) {
  if (x < 0) putchar('-'), x = -x;
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int N = 300100, P = 998244353;

inline int qpow(int x, int y) {
  int res(1);
  while (y) {
    if (y & 1) res = 1ll * res * x % P;
    x = 1ll * x * x % P;
    y >>= 1;
  }
  return res;
}

int r[N];

void ntt(int *x, int lim, int opt) {
  register int i, j, k, m, gn, g, tmp;
  for (i = 0; i < lim; ++i)
    if (r[i] < i) swap(x[i], x[r[i]]);
  for (m = 2; m <= lim; m <<= 1) {
    k = m >> 1;
    gn = qpow(3, (P - 1) / m);
    for (i = 0; i < lim; i += m) {
      g = 1;
      for (j = 0; j < k; ++j, g = 1ll * g * gn % P) {
        tmp = 1ll * x[i + j + k] * g % P;
        x[i + j + k] = (x[i + j] - tmp + P) % P;
        x[i + j] = (x[i + j] + tmp) % P;
      }
    }
  }
  if (opt == -1) {
    reverse(x + 1, x + lim);
    register int inv = qpow(lim, P - 2);
    for (i = 0; i < lim; ++i) x[i] = 1ll * x[i] * inv % P;
  }
}

int A[N], B[N], C[N];

char a[N], b[N];

int main() {
  register int i, lim(1), n;
  scanf("%s", &a);
  n = strlen(a);
  for (i = 0; i < n; ++i) A[i] = a[n - i - 1] - '0';
  while (lim < (n << 1)) lim <<= 1;
  scanf("%s", &b);
  n = strlen(b);
  for (i = 0; i < n; ++i) B[i] = b[n - i - 1] - '0';
  while (lim < (n << 1)) lim <<= 1;
  for (i = 0; i < lim; ++i) r[i] = (i & 1) * (lim >> 1) + (r[i >> 1] >> 1);
  ntt(A, lim, 1);
  ntt(B, lim, 1);
  for (i = 0; i < lim; ++i) C[i] = 1ll * A[i] * B[i] % P;
  ntt(C, lim, -1);
  int len(0);
  for (i = 0; i < lim; ++i) {
    if (C[i] >= 10) len = i + 1, C[i + 1] += C[i] / 10, C[i] %= 10;
    if (C[i]) len = max(len, i);
  }
  while (C[len] >= 10) C[len + 1] += C[len] / 10, C[len] %= 10, len++;
  for (i = len; ~i; --i) putchar(C[i] + '0');
  puts("");
  return 0;
}

贡献者:@ChungZH@Yukimaikoriya

本页面最近更新:2/3/2023, 12:00:00 AM更新历史

发现错误?想一起完善? 在 GitHub 上编辑此页!

本页面的全部内容在 CC BY-SA 4.0SATA 协议之条款下提供,附加条款亦可能应用

评论

0 条评论
未登录用户


Copyright © 2016 - 2023 OI Wiki Team

最近更新:cbb3fd4, 2023-02-03

联系方式:Telegram 群组 / QQ 群组