任意模数多项式乘法-MTT

拆系数FFT

在之前,我们用 原根 代替 单位根,实现了比 FFT 更快的 NTT

NTT 的限制比较大:模数能表示为 \(a\times 2^k+1\),通常这个 \(k\) 要求大于 \(16\)

如果我们换了一个不太友好的模数(如 \(10^9+7\), 19190817),似乎 NTT 就发挥不了了

有一种跑 \(9\)NTT实现方法,但常数大 + \(10^{26}\) 的值域 long long 也存不下,不太好实现

其实我们这里的瓶颈就是精度的问题,如果我们可以将值域缩减,精度就可以提高,通过跑 FFT 再取模也是可以的

一种做法就是将系数拆成 \(a_1\times 2^{15}+b_1\) 的形式

那么卷积的时候就是 \((a_1\times 2^{15}+b_1)(a_2\times 2^{15}+b_2)=a_1a_2\times 2^{30}+(a_1b_2+a_2b_1)\times 2^{15}+b_1b_2\)

我们可以考虑将 \(a_1,b_1,a_2,b_2\) 都用多项式表示出来

但这样我们需要 \(8\) 次的 FFT

参考 三次变两次 的做法,我们考虑构造复多项式 \(P=A_1+B_1i\), \(Q=A_2+B_2i\)

那么 \(P*Q=A_1A_2-B_1B_2+(A_1B_2+A_2B_1)i\),这样就得到 \(2^{15}\) 的那一项了

再构造 \(P\) 的共轭多项式 \(P'=A_1-B_1i\),有 \(P'Q=A_1A_2+B_1B_2-(A_1B_2-A_2B_1)i\)

\(PQ+P'Q\) 可得 \(2A_1A_2\)\(P'Q-PQ\) 可得 \(2B_1B_2\)

这样就可以跑出来了,多项式乘积的值域就是 \(10^{14}\) 的了

代码

忠告:

  1. std::cosstd::sin 的精度比 cossin 的要高(慢点无所谓,但得能卡过去啊)

  2. 在点值相乘的时候就除以 \(len\),别 IDFT 后再除(为了降低值域,把精度卡上去)

  3. 强制转换时要记得 floor (四舍五入),并马上取模(防止爆 long long

(卡精度卡到自闭了,还不如去学 3模数NTT

练习题:

P4239 任意模数多项式乘法逆

(没啥好说的,就是MTT+多项式逆元而已)

posted @ 2022-04-26 15:50  zuytong  阅读(65)  评论(0编辑  收藏  举报