概述
对于多项式 f(x)。
求逆:若存在 g(x) 满足 f(x)g(x) ≡ 1 (mod xn), 则称 g(x) 为 f(x) 在模 xn 意义下的逆元, 记作 f-1(x)。
对数:可以将其对数函数看做其与麦克劳林级数的复合:
指数:
首先, 对于 b>a, A(x) 在模 xb 下的逆元, 同时也是 A(x) 在模 xa 下的逆元。
于是可以求出模 x2k 下的逆元, 以下介绍倍增法。
首先, A(x) ≡ c (mod x), c 是一个常数, 则 A-1(x) (mod x) 就是 c-1。
然后, 假设:
由于显然有 \(A(x)B(x) \equiv 1 \mod x^{n/2}\)
于是有:\(A(x)(B(x)-B‘(x)) \equiv 0 \mod x^{n/2}\), 即 \(B(x)-B‘(x) \equiv 0 \mod x^{n/2}\)
然后两边平方:\(B(x)^2 + B‘(x)^2 - 2B(x)B‘(x) \equiv 0 \mod x^n\)
模数也可以一起平方的原因, 详见 miskoo的 blog
然后乘上 A(x) 并移项:\(B(x) \equiv 2B‘(x) - A(x)B‘(x)^2 \mod x^n\)
这就是倍增的过程了。
时间复杂度是 \(T(n) = T(n/2) + O(n\log n) = O(n\log n)\), 注意 \(T(n/2)\) 前面没有乘以二, 所以总复杂度不是 \(O(n\log^2 n)\)。
实际代码实现的时候, 其实模数的次幂可以是任意正整数(证明同 miskoo 的证明), 只需要求出模 \(x^{\lceil n/2\rceil}\) 下的逆, 就可以用上述方法求出模 \(x^n\) 下的逆。
// 交板子过了, 保证正确
void poly_inv(int deg, int *a, int *b) {
if(deg == 1) { b[0] = ksm(a[0], mo-2); return; }
poly_inv((deg + 1) >> 1, a, b);
int len = 1; while(len < (deg << 1)) len = len << 1;
for(int i=0; i<deg; ++i) c[i] = a[i];
for(int i=deg; i<len; ++i) c[i] = 0;
for(int i=1; i<len; ++i) rv[i] = (rv[i>>1]>>1) | (i&1?len>>1:0);
NTT(c, len, 1), NTT(b, len, 1);
for(int i=0; i<len; ++i) b[i] = (LL)b[i] * (2ll - (LL)c[i] * b[i] % mo) % mo;
NTT(b, len, -1);
for(int i=deg; i<len; ++i) b[i] = 0;
}
原文:https://www.cnblogs.com/tztqwq/p/14323909.html