====== 快速数论变换(NTT) ======
===== 原理 =====
[[https://oi-wiki.org/math/poly/ntt/|OI Wiki-快速数论变换(NTT)]]
===== 例题 =====
==== 例 1 ====
=== 题意 ===
[[https://www.luogu.com.cn/problem/P3803|P3803 【模板】多项式乘法(FFT)]]
给定一个 $n$ 次多项式 $F(x)$,和一个 $m$ 次多项式 $G(x)$。求出 $F(x)$ 和 $G(x)$ 的卷积。$1\le n,m\le 10^6$
=== 题解 ===
找到模数 $p$,使得 $p=qn+1,(n=2^k)$,对于模 $p$ 的原根 $g$,$\displaystyle g_n\equiv g^q\equiv g^{\frac{p-1}{n}}$,可以看作 FFT 中 $n$ 次单位根 $\omega_n$ 的等价,因为它们都是各自所在群的生成元。所以 NTT 的代码只需把 FFT 中的 $\omega_n$ 都用 $g^{\frac{p-1}{n}}$ 替换掉就好了。
=== 评价 ===
NTT 模板题
=== 代码 ===
#include
using namespace std;
const int N=3*1e6+10,P=998244353,G=3,Gi=332748118;// 原根 3 及其逆元
typedef long long ll;
int rev[N];
ll a[N],b[N];
ll fastpow(ll x,ll y){// 快速幂
ll ret=1;
for(;y;y>>=1,x=x*x%P)
if(y&1) ret=ret*x%P;
return ret;
}
void NTT(ll *y,int len,int on){// 与 FFT 的代码类似
for(int i=0;i>1]>>1)|((i&1)<<(l-1));// 蝴蝶变换
NTT(a,len,1);
NTT(b,len,1);
for(int i=0;i
==== 例 2 ====
=== 题意 ===
[[https://www.luogu.com.cn/problem/P1919|P1919 【模板】A*B Problem升级版(FFT快速傅里叶)]]
给你两个正整数 $a,b$,求 $a\times b$。$1\le a,b\le 10^{1000000}$
=== 题解 ===
使用 NTT 求解本题
=== 评价 ===
NTT 模板题
=== 代码 ===
#include
using namespace std;
typedef long long ll;
const int N=1e7+5;
const ll P=998244353,G=3,Gi=332748118;
int rev[N];
char s1[N],s2[N];
ll a[N],b[N];
int c[N];
ll fastpow(ll x,ll y){
ll ret=1;
for(;y;y>>=1,x=x*x%P)
if(y&1) ret=ret*x%P;
return ret;
}
void NTT(ll *y,int len,int on){
for(int i=0;i>1]>>1)|((i&1)<<(l-1));
NTT(a,len,1);
NTT(b,len,1);
for(int i=0;i=0;i--) printf("%d",c[i]);
return 0;
}
==== 例 3 ====
=== 题意 ===
[[https://www.luogu.com.cn/problem/P4245|P4245 【模板】任意模数多项式乘法]]
给定 $2$ 个多项式 $F(x),G(x)$,请求出 $F(x)*G(x)$,系数对 $p$ 取模,且不保证 $p$ 可以分解成 $p=a\cdot 2^k+1$ 的形式。$1\le n,m\le 10^5,0\le a_i,b_i\le 10^9,2\le p\le 10^9+9$。
=== 题解 ===
若不对 $p$ 取模,则系数最大值可达到 $np^2=10^{23}$,考虑使用 NTT 求解,选取 $3$ 个模数 $p_1,p_2,p_3$,使得 $p_1p_2p_3>np^2$,分别求出这 $3$ 个模下的系数 $x$,然后通过中国剩余定理合并答案,最后再对 $p$ 取模得到最终答案。过程中注意防止 long long 溢出。
=== 评价 ===
MTT 模板题
=== 代码 ===
#include
using namespace std;
const int N=1e5+5;
typedef long long ll;
int rev[N<<2];
ll n,m;
ll a[N<<2],b[N<<2],c[N<<2];
ll a1[N<<2],b1[N<<2];
ll x[4][N<<2];
const ll p[4]={0,998244353,2281701377,469762049},G=3;// 3 个都有原根 3 的大模数
const ll p_12=p[1]*p[2];
ll fastpow(ll x,ll y,ll mod){
ll ret=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1) ret=ret*x%mod;
return ret;
}
const ll inv1=fastpow(p[1],p[2]-2,p[2]),inv2=fastpow(p_12%p[3],p[3]-2,p[3]);
void NTT(ll *y,int len,int on,ll P){
ll Gi=fastpow(G,P-2,P);
for(int i=0;i>1]>>1)|((i&1)<<(l-1));
MTT(a,b,len,P,c);
for(int i=0;i<=n+m;i++)
printf("%lld ",c[i]);
return 0;
}