- 概念引入

  - 阶
    对于$p \in N_+$且$(a, \ p) = 1$,满足$a^r \equiv 1 (mod \ p)$的最小的非负$r$为$a$模$p$意义下的阶,记作$\delta_p(a)$


  - 原根
    定义:若$p \in N_+$且$a \in N$,若$\delta_p(a) = \phi(p)$,则称$a$为模$p$的一个原根
    相关定理:
      - 若一个数$m$拥有原根,那么它必定为$2, \ 4, \ p^t, \ 2p^t \ (p$为奇质数$)$的其中一个
      - 每个数$p$都有$\phi(\phi(p))$个原根
      证明:若$p \in N_+$且$(a, \ p) = 1$,正整数$r$满足$a^r \equiv 1 (mod \ p)$,那么$\delta(p) | r$,由此推广,可知$\delta(p) | \phi(p)$,所以$p$的原根个数即为$p$之前与$\phi(p)$互质的数,即$\phi(p)$故定理成立
      - 若$g$是$m$的一个原根,则$g, \ g^1, \ g^2, \ ..., \ g^{\phi(m)} (mod \ p)$两两不同
    原根求法:
      将$\phi(m)$质因数分解,得$\phi(m) = p_1^{c_1} * p_2^{c_2} * ... * p_k^{c_k}$
      那么所有$g$满足$g^{\frac{\phi(m)}{p_i}} \neq 1 (mod \ m)$即为$m$的原根

 

- $NTT$

  由于$FTT$涉及到复数的运算,所以常数很大,而$NTT$仅需使用长整型,可大大优化常数

  能够将原根代替单位根进行计算,是因为它们的性质相似,至少在单位根需要的那几个性质原根都满足,当然,要能够进行$NTT$,需要满足模数$p$为质数,且$p = ax + 1$其中$x$为$2$的次幂,那么一般能满足条件的数(常用)有:
  $|\ \ \ \ \ \ \ \ \ \ \ \ p \ \ \ \ \ \ \ \ \ \ \ \ |\ \ \ \ g \ \ \ \ |$

  $|\ \ \ \ 469762049 \ \ \ \ |\ \ \ \ 3 \ \ \ \ |$

  $|\ \ \ \ 998244353 \ \ \ \ |\ \ \ \ 3 \ \ \ \ |$

  $|\ \ \ 1004535809 \ \ \ |\ \ \ \ 3 \ \ \ \ |$
  那么,就可以将单位根$\omega_n$替换为$g^{\frac{p - 1}{n}}$进行$NTT$了

 

- 代码 

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <cmath>
  6 
  7 #define MOD 998244353
  8 #define g 3
  9 
 10 using namespace std;
 11 
 12 typedef long long LL;
 13 
 14 const int MAXN = (1 << 22);
 15 
 16 LL power (LL x, int p) {
 17     LL cnt = 1;
 18     while (p) {
 19         if (p & 1)
 20             cnt = cnt * x % MOD;
 21 
 22         x = x * x % MOD;
 23         p >>= 1;
 24     }
 25 
 26     return cnt;
 27 }
 28 
 29 const LL invg = power (g, MOD - 2);
 30 
 31 int N, M;
 32 LL A[MAXN], B[MAXN];
 33 
 34 int oppo[MAXN];
 35 int limit;
 36 void NTT (LL* a, int inv) {
 37     for (int i = 0; i < limit; i ++)
 38         if (i < oppo[i])
 39             swap (a[i], a[oppo[i]]);
 40     for (int mid = 1; mid < limit; mid <<= 1) {
 41         LL ome = power (inv == 1 ? g : invg, (MOD - 1) / (mid << 1));
 42         for (int n = mid << 1, j = 0; j < limit; j += n) {
 43             LL x = 1;
 44             for (int k = 0; k < mid; k ++, x = x * ome % MOD) {
 45                 LL a1 = a[j + k], xa2 = x * a[j + k + mid] % MOD;
 46                 a[j + k] = (a1 + xa2) % MOD;
 47                 a[j + k + mid] = (a1 - xa2 + MOD) % MOD;
 48             }
 49         }
 50     }
 51 }
 52 
 53 int getnum () {
 54     int num = 0;
 55     char ch = getchar ();
 56 
 57     while (! isdigit (ch))
 58         ch = getchar ();
 59     while (isdigit (ch))
 60         num = (num << 3) + (num << 1) + ch - '0', ch = getchar ();
 61 
 62     return num;
 63 }
 64 
 65 int main () {
 66     N = getnum (), M = getnum ();
 67     for (int i = 0; i <= N; i ++)
 68         A[i] = (int) getnum ();
 69     for (int i = 0; i <= M; i ++)
 70         B[i] = (int) getnum ();
 71 
 72     int n, lim = 0;
 73     for (n = 1; n <= N + M; n <<= 1, lim ++);
 74     for (int i = 0; i <= n; i ++)
 75         oppo[i] = (oppo[i >> 1] >> 1) | ((i & 1) << (lim - 1));
 76     limit = n;
 77     NTT (A, 1);
 78     NTT (B, 1);
 79     for (int i = 0; i <= n; i ++)
 80         A[i] = A[i] * B[i] % MOD;
 81     NTT (A, - 1);
 82     LL invn = power (n, MOD - 2);
 83     for (int i = 0; i <= N + M; i ++) {
 84         if (i)
 85             putchar (' ');
 86         printf ("%d", (int) (A[i] * invn % MOD));
 87     }
 88     puts ("");
 89 
 90     return 0;
 91 }
 92 
 93 /*
 94 1 2
 95 1 2
 96 1 2 1
 97 */
 98 
 99 /*
100 5 5
101 1 7 4 0 9 4
102 8 8 2 4 5 5
103 */
NTT

相关文章:

  • 2021-11-30
  • 2021-09-29
  • 2021-10-30
  • 2021-07-27
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
猜你喜欢
  • 2021-05-04
  • 2022-01-22
  • 2022-02-26
  • 2022-02-20
  • 2022-12-23
相关资源
相似解决方案