- 概念引入

  - 阶
    对于$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$了

- 代码 

 #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath> #define MOD 998244353
#define g 3 using namespace std; typedef long long LL; const int MAXN = ( << ); LL power (LL x, int p) {
LL cnt = ;
while (p) {
if (p & )
cnt = cnt * x % MOD; x = x * x % MOD;
p >>= ;
} return cnt;
} const LL invg = power (g, MOD - ); int N, M;
LL A[MAXN], B[MAXN]; int oppo[MAXN];
int limit;
void NTT (LL* a, int inv) {
for (int i = ; i < limit; i ++)
if (i < oppo[i])
swap (a[i], a[oppo[i]]);
for (int mid = ; mid < limit; mid <<= ) {
LL ome = power (inv == ? g : invg, (MOD - ) / (mid << ));
for (int n = mid << , j = ; j < limit; j += n) {
LL x = ;
for (int k = ; k < mid; k ++, x = x * ome % MOD) {
LL a1 = a[j + k], xa2 = x * a[j + k + mid] % MOD;
a[j + k] = (a1 + xa2) % MOD;
a[j + k + mid] = (a1 - xa2 + MOD) % MOD;
}
}
}
} int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int main () {
N = getnum (), M = getnum ();
for (int i = ; i <= N; i ++)
A[i] = (int) getnum ();
for (int i = ; i <= M; i ++)
B[i] = (int) getnum (); int n, lim = ;
for (n = ; n <= N + M; n <<= , lim ++);
for (int i = ; i <= n; i ++)
oppo[i] = (oppo[i >> ] >> ) | ((i & ) << (lim - ));
limit = n;
NTT (A, );
NTT (B, );
for (int i = ; i <= n; i ++)
A[i] = A[i] * B[i] % MOD;
NTT (A, - );
LL invn = power (n, MOD - );
for (int i = ; i <= N + M; i ++) {
if (i)
putchar (' ');
printf ("%d", (int) (A[i] * invn % MOD));
}
puts (""); return ;
} /*
1 2
1 2
1 2 1
*/ /*
5 5
1 7 4 0 9 4
8 8 2 4 5 5
*/

NTT

- 任意模数$NTT$(三模数$NTT$法)

  有公式

$\left\{\begin{aligned} x \equiv a_1 (mod \ m_1) \\ x \equiv a_2 (mod \ m_2) \\ x \equiv a_3 (mod \ m_3) \end{aligned}\right.$

  直接乘会爆$long \ long$,就先将上面的用$CRT$合并,得

$\left\{\begin{aligned} x \equiv A (mod \ M) \\ x \equiv a_3 (mod \ m_3) \end{aligned}\right.$

  那么设$Ans = kM + A$,则有
  

$kM + A \equiv a_3 (mod \ m_3)$
$\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ k \equiv (a_3 - A)M^{- 1} (mod \ m_3)$

  直接处理即可

- 代码(任意模数$NTT$)

 #include <iostream>
#include <cstdio>
#include <cstring> using namespace std; typedef long long LL; const int MAXN = ( << ); const LL MOD[]= {, , }; // 三模数
const LL g = ;
const long double eps = 1e-; LL multi (LL a, LL b, LL p) { // 快速乘
a %= p, b %= p;
return ((a * b - (LL) ((LL) ((long double) a / p * b + eps) * p)) % p + p) % p;
}
LL power (LL x, LL p, LL mod) {
LL cnt = ;
while (p) {
if (p & )
cnt = cnt * x % mod; x = x * x % mod;
p >>= ;
} return cnt;
}
const LL invg[]= {power (g, MOD[] - , MOD[]), power (g, MOD[] - , MOD[]), power (g, MOD[] - , MOD[])}; int N, M;
LL P; LL A[MAXN], B[MAXN]; int limit;
int oppo[MAXN];
void NTT (LL* a, int inv, int type) {
for (int i = ; i < limit; i ++)
if (i < oppo[i])
swap (a[i], a[oppo[i]]);
for (int mid = ; mid < limit; mid <<= ) {
LL ome = power (inv == ? g : invg[type], (MOD[type] - ) / (mid << ), MOD[type]);
for (int n = mid << , j = ; j < limit; j += n) {
LL x = ;
for (int k = ; k < mid; k ++, x = x * ome % MOD[type]) {
LL a1 = a[j + k], xa2 = x * a[j + k + mid] % MOD[type];
a[j + k] = (a1 + xa2) % MOD[type];
a[j + k + mid] = (a1 - xa2 + MOD[type]) % MOD[type];
}
}
}
} LL ntta[][MAXN], nttb[][MAXN];
void NTT_Main () {
int n, lim = ;
for (n = ; n <= N + M; n <<= , lim ++);
limit = n;
for (int i = ; i < n; i ++)
oppo[i] = (oppo[i >> ] >> ) | ((i & ) << (lim - ));
for (int i = ; i < ; i ++) {
for (int j = ; j < n; j ++)
ntta[i][j] = A[j];
for (int j = ; j < n; j ++)
nttb[i][j] = B[j];
NTT (ntta[i], , i);
NTT (nttb[i], , i);
for (int j = ; j < n; j ++)
ntta[i][j] = ntta[i][j] * nttb[i][j] % MOD[i];
NTT (ntta[i], - , i);
LL invn = power (n, MOD[i] - , MOD[i]);
for (int j = ; j <= N + M; j ++)
ntta[i][j] = ntta[i][j] * invn % MOD[i];
}
} LL ans[MAXN];
void CRT () {
LL m = MOD[] * MOD[];
LL M1 = MOD[], M2 = MOD[];
LL t1 = power (M1, MOD[] - , MOD[]), t2 = power (M2, MOD[] - , MOD[]), invM = power (m % MOD[], MOD[] - , MOD[]);
for (int i = ; i <= N + M; i ++) {
LL a1 = ntta[][i], a2 = ntta[][i], a3 = ntta[][i];
LL A = (multi (a1 * M1 % m, t1 % m, m) + multi (a2 * M2 % m, t2 % m, m)) % m;
LL k = ((a3 - A % MOD[]) % MOD[] + MOD[]) % MOD[] * invM % MOD[];
ans[i] = ((k % P * (m % P) % P + A % P) % P + P) % P;
}
} int getnum () {
int num = ;
char ch = getchar (); while (! isdigit (ch))
ch = getchar ();
while (isdigit (ch))
num = (num << ) + (num << ) + ch - '', ch = getchar (); return num;
} int main () {
N = getnum (), M = getnum (), P = (LL) getnum ();
for (int i = ; i <= N; i ++)
A[i] = (LL) getnum ();
for (int i = ; i <= M; i ++)
B[i] = (LL) getnum (); NTT_Main ();
CRT ();
for (int i = ; i <= N + M; i ++) {
if (i)
putchar (' ');
printf ("%lld", ans[i]);
}
puts (""); return ;
} /*
5 8 28
19 32 0 182 99 95
77 54 15 3 98 66 21 20 38
*/

任意模数NTT(三模数NTT法)

$NTT$(快速数论变换)的更多相关文章

  1. NTT 快速数论变换

    NTT 先学习FFT 由于FFT是使用复数运算,精度并不好,而且也无法取模,所以有了NTT(快速数论变换). 建议先完全理解FFT后再学习NTT. 原根 NTT使用与单位根性质相似的原根来代替单位根. ...

  2. [学习笔记]NTT——快速数论变换

    先要学会FFT[学习笔记]FFT——快速傅里叶变换 一.简介 FFT会爆精度.而且浮点数相乘常数比取模还大. 然后NTT横空出世了 虽然单位根是个好东西.但是,我们还有更好的东西 我们先选择一个模数, ...

  3. 模板 NTT 快速数论变换

    NTT裸模板,没什么好解释的 这种高深算法其实也没那么必要知道原理 #include <cstdio> #include <cstring> #include <algo ...

  4. 【算法】快速数论变换(NTT)初探

    [简介] 快速傅里叶变换(FFT)运用了单位复根的性质减少了运算,但是每个复数系数的实部和虚部是一个余弦和正弦函数,因此系数都是浮点数,而浮点数的运算速度较慢且可能产生误差等精度问题,因此提出了以数论 ...

  5. Algorithm: 多项式乘法 Polynomial Multiplication: 快速傅里叶变换 FFT / 快速数论变换 NTT

    Intro: 本篇博客将会从朴素乘法讲起,经过分治乘法,到达FFT和NTT 旨在能够让读者(也让自己)充分理解其思想 模板题入口:洛谷 P3803 [模板]多项式乘法(FFT) 朴素乘法 约定:两个多 ...

  6. 「算法笔记」快速数论变换(NTT)

    一.简介 前置知识:多项式乘法与 FFT. FFT 涉及大量 double 类型数据操作和 \(\sin,\cos\) 运算,会产生误差.快速数论变换(Number Theoretic Transfo ...

  7. 快速傅里叶变换 & 快速数论变换

    快速傅里叶变换 & 快速数论变换 [update 3.29.2017] 前言 2月10日初学,记得那时好像是正月十五放假那一天 当时写了手写版的笔记 过去近50天差不多忘光了,于是复习一下,具 ...

  8. JZYZOJ 2041 快速数论变换 NTT 多项式

    http://172.20.6.3/Problem_Show.asp?id=2041 https://blog.csdn.net/ggn_2015/article/details/68922404 代 ...

  9. 多项式乘法(FFT)模板 && 快速数论变换(NTT)

    具体步骤: 1.补0:在两个多项式最前面补0,得到两个 $2n$ 次多项式,设系数向量分别为 $v_1$ 和 $v_2$. 2.求值:用FFT计算 $f_1 = DFT(v_1)$ 和 $f_2=DF ...

随机推荐

  1. JVM堆内存控制/分代垃圾回收

    JVM的堆的内存, 是通过下面面两个参数控制的 -Xms 最小堆的大小, 也就是当你的虚拟机启动后, 就会分配这么大的堆内存给你 -Xmx 是最大堆的大小 当最小堆占满后,会尝试进行GC,如果GC之后 ...

  2. [AT2000] [agc002_f] Leftmost Ball

    题目链接 AtCoder:https://agc002.contest.atcoder.jp/tasks/agc002_f 洛谷:https://www.luogu.org/problemnew/sh ...

  3. [TJOI2013]单词 AC自动机

    题面: 洛谷 题解: 很久之前做的题了,只不过之前一直90....最近才发现是哪里写错了. 我们对字符集建AC自动机. 首先考虑一个暴力的做法,把文章当做一个长串,直接在自动机上跳,但是我们会发现,这 ...

  4. 【Treeview】遍历本地磁盘

    一.前言 Treeview控件常用于遍历本地文件信息,通常与Datagridview与ImageList搭配.ImageList控件用于提供小图片给TreeView控件,DatagridView通常显 ...

  5. 【BZOJ3504】危桥(网络流)

    [BZOJ3504]危桥(网络流) 题面 BZOJ 洛谷 Description Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1.某些岛屿之间有桥相连,桥上的道路是双 向的, ...

  6. <深入理解计算机系统>第七章读书笔记

    第七章读书笔记 链接 链接:将各种代码和数据部分收集起来并组合成为一个单一文件的过程.(这个文件可被加载或拷贝到存储器并执行) 链接可以执行于编译,加载或运行时. 静态链接: 两个主要任务: 1 符号 ...

  7. 公告:开通csdn博客,敬请关注!

    公告:开通csdn博客,敬请关注!地址:https://blog.csdn.net/cyjch

  8. grep index.php *

    zb@zb-computer:/usr/local/nginx/conf/vhost$ grep index.php * caomall17.conf: index index.html index. ...

  9. 堡垒机初识--paramiko模块

    一.paramiko模块 Python的paramiko模块基于SSH用于连接远程服务器并执行相关的操作. 1.1 在windows上安装paramiko模块 测试环境: win10 , python ...

  10. Bootstrap 排版 文本

    小一点浅一点(设置文本为父文本大小的 85%)<small> .small 大一点粗一点 行高高一点 .lead 粗  <strong> 斜体 <em> 添加背景色 ...