前些天学了一堆多项式的算法,今天总结一下。
##乘法
朴素的NTT就不说了,主要说一下三模数NTT。
三模数NTT,顾名思义,就是选取三个适合做NTT的模数,然后把它们用CRT合并起来得到的答案再去对我们要求的模数取模。
为了方便,这三个模数分别是998244353, 1004535809和469762049。它们的原根都是3,且它们减去1的值都有超过20个2的因子。
如果你觉得能用int128的话就请忽略下面所有的表述
但是,在用CRT合并的时候,我们遇到一个麻烦:这三个模数的乘积太大了。我们令三个模数分别为p1, p2, p3,对三个模数取模得到的答案分别为a1, a2, a3,不做取模的原本的答案为Ans,则有:
A n s ≡ a 1 ( m o d p 1 ) Ans \equiv a1~(mod~p1) Ans≡a1 (mod p1)
A n s ≡ a 2 ( m o d p 2 ) Ans \equiv a2~(mod~p2) Ans≡a2 (mod p2)
A n s ≡ a 3 ( m o d p 3 ) Ans \equiv a3~(mod~p3) Ans≡a3 (mod p3)
先用CRT合并前两个模数的答案,得到
A n s ≡ A ( m o d M ) Ans \equiv A~(mod~M) Ans≡A (mod M)
A n s ≡ a 3 ( m o d p 3 ) Ans \equiv a3~(mod~p3) Ans≡a3 (mod p3)
设
A n s = k M + A Ans = kM + A Ans=kM+A
那么
k M + A ≡ a 3 ( m o d p 3 ) kM + A \equiv a3~(mod~p3) kM+A≡a3 (mod p3)
即
k ≡ ( a 3 − A ) M ( m o d p 3 ) k \equiv \frac{(a3 - A)}{M}~(mod~p3) k≡M(a3−A) (mod p3)
此时k, M, A我们都已经得到,就直接对我们要求的模数取模就好了,即
A n s % p = ( ( k % p ) ∗ ( M % p ) + A ) % p Ans~\%~p = ((k~\%~p) * (M~\%~p) + A)~\%~p Ans % p=((k % p)∗(M % p)+A) % p
##Code
inline void exntt(int *a, int *b)
{
int len = 1, bit = 0;
while (len < n + m) len <<= 1, bit++;
for (int i = 0; i < len; i++) rev[i] = rev[i >> 1] >> 1 | (i & 1) << bit - 1;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < len; j++) c[j] = a[j], d[j] = b[j];
ntt(c, len, 1, p[i]);
ntt(d, len, 1, p[i]);
for (int j = 0; j < len; j++) ans[i][j] = 1ll * c[j] * d[j] % p[i];
ntt(ans[i], len, -1, p[i]