多项式操作汇总
最近学了不少多项式的小 trick,感觉不记一下容易忘。那我就把做法都摆在这里吧,代码还是自己去写了。(鉴于所有操作都是建立在 FFT 之上的,所以会写 FFT 的话所有东西的代码都能写出来了)
多项式乘法
这个就是一切的基础,先把这个代码搞熟后面就都能写啦。
多项式逆元
定义多项式 (g(x)) 的逆元 (g^{-1}(x))(以下为了方便简记为 (f(x)))是满足如下等式的多项式:
可以想象,模 (x^n) 的意思就是把 (x^{n+1}) 及更高的项都删掉,剩下的部分。为什么我们可以这样做呢?因为在解决实际问题的时候我们都只关心前 (n) 项(即 (x^0 sim x^{n-1}) 的系数)。
求 (f(x)) 的方法可能不止一种,但是我觉得用泰勒展开这个方法虽然麻烦但是好记(因为大多数问题可以用它——它是“通解”)。
注意:以下多项式都会在第一次用 (f(x)) 表示过后就可能省掉后面的 ((x)),你需要知道它是一个多项式。
我们先构造一个函数 (H(f) = f^{-1} - g)。你没有看错,这个 (H(f)) 函数的自变量就是一个多项式。为什么要这样构造函数呢?因为可以发现当自变量 (f = g^{-1}),即 (f) 就是答案的时候,(H(f) = 0)。
我们用倍增的方法求这个 (f),假设已经求出 (f_0(x)) 是 (f) 前 (lceil frac{n}{2} ceil) 项,即
现在我们要用 (f_0) 和 (g) 表示出 (f)。利用我们刚才构造的函数,根据 (f_0) 的定义有
为了实现上面黑体字的目的,我们对 (H(f)) 在 (f_0) 处进行泰勒展开(想点进去看的请翻到文章末尾),即用下面这个式子去逼近 (H(f))
注意上面这个等式不是模意义下的,不需要取模它们就相等。
接下来我们观察一下 ((f-f_0)^i),由于 (f - f_0 equiv 0 (mod x^{lceil frac{n}{2} ceil})),发现当 (i = 2) 时,最高次项平方之后次数一定 (ge n),所以有 (forall i > 1, (f-f_0)^i equiv 0 (mod x^n)),那么上面的泰勒展开的无穷多项就只剩了两项,整理出来就是
移项得到
至此我们得到了牛顿迭代的公式,以后可以记它,就不用每次都泰勒展开了。接下来的工作就是把刚才构造的 (H(f)) 带进去。
递归下去,再用这个式子回溯,就求出来啦!
时间复杂度:(T(n) = T(frac{n}{2}) + O(nlog n) = O(nlog n)),发现这个小秘密后,我们可以无限套,理论复杂度不变
多项式除法 & 取模
注意这里的除法不是在模意义下的!所以它会有商和余数,即如果我要求 (frac{A(x)}{B(x)}),我就是要求两个多项式 (D(x)) 和 (R(x)),使得它们满足下面这样一个等式
现在我们记 (n) 为 (A) 的次数,(m) 为 (B) 的次数,那么有 (D) 的次数为 (mathrm{max}(n - m, 0)),(R) 的次数 (le m - 1)。
记 (F(x)) 的次数为 (t), (F^R(x) = F(frac{1}{x}) cdot x^t),直观理解就是把 (F) 的系数反转一下。
接下来将 (frac{1}{x}) 带入到原式中,再两边同乘 (x^n),看看会发生什么
如果我们将新的等式放在模 (x^{n-m+1}) 的意义下,(R(x)) 就被消掉了!与此同时 (D^R(x)) 最高次为 (x^{n-m}),故不会受到任何影响,即模意义下求出的 (D^R(x)) 与非模意义下的完全相同。
(D(x)) 就是 (D^R(x)) 反转一下系数,把 (D(x)) 代到最初的式子里就能得到 (R(x)) 了。
多项式多点求值
任务:给出多项式 (F(x)) 和集合 ({ x_1, x_2, cdots , x_n }),要求返回一个集合 ({ F(x_1), F(x_2), cdots , F(x_n) })。
构造多项式 (A(x) = (x - x_1)(x - x_2)cdots(x - x_n)),令 (G(x) equiv F(x) (mod A(x))),那么对于 (forall i in [1, n], G(x_i) = F(x_i))。
为什么呢?因为
而 (forall i in [1, n], A(x_i) = 0, herefore F(x_i) = G(x_i))。
于是就可以分治了。令 (A_l(x) = prod_{i = 1}^{lfloor frac{n}{2} floor} { (x - x_i) }, A_r(x) = prod_{i = lfloor frac{n}{2} floor + 1}^n { (x - x_i) }),然后把 (F(x) mod A_l(x)) 和 (F(x) mod A_r(x)) 扔下去递归了。
时间复杂度:(O(n log^2 n))
多项式 ln & exp
应该有不少人好奇这两个操作的组合意义是什么。事实上这个问题我并不是很清楚,我们也许可以通过泰勒展开后的系数研究一下它的组合意义,但它们的价值主要不在于组合意义,而在于其他方面的应用。
具体应用在讲完做法后立刻就会有一个,所以不必着急,我们先看看多项式取 ln 怎么求。
多项式取 ln
注意到这样一个等式
于是积分一下就可以轻易地求出 (ln f(x)) 了
其中,求导和积分都能在 (O(n)) 时间内求出,所以总时间复杂度 (O(n))。
细心的读者一定会问:积分之后,常数项如何确定?注意到这里的 ([x^0]f(x)) 必须为 (1),也就是说 ([x^0] ln f(x)) 一定是 (0),那么积分完后常数项为 (0) 就好了。(如果 ([x^0]f(x) e 1) 怎么求?QAQ 我也不会,因为我们似乎很难找到 (ln c (c > 0)) 在剩余系下的表示……)
多项式求 exp
我们要求这样的一个函数 (f(x)),满足
egin{matrix}
f(x) equiv e^{g(x)} & (mod x^n)
end{matrix}
两边取对数,我们有
egin{matrix}
ln f(x) equiv g(x) & (mod x^n)
end{matrix}
我们还是考虑倍增求 (f),套用上面泰勒展开推出来的结果,构造函数 (H(f) = ln f - g),令 (f_0 equiv f (mod x^{lceil frac{n}{2} ceil})),于是得到
egin{matrix}
f equiv f_0 - frac{ln f_0 - g}{f_0^{-1}} & (mod x^n) \
f equiv f_0(1 - ln f_0 + g) & (mod x^n)
end{matrix}
这样就 (O(n log n)) 解决啦,你会发现求 exp 其实依赖于求 ln。同样地我们只能求 ([x^0]g(x) = 0) 的 (e^{g(x)})。
接下来就是一个经典的应用。
多项式乘方
求 (f(x)) 满足
egin{matrix}
f(x) equiv g(x) ^ k & (mod x^n)
end{matrix}
指数相关,考虑两边取对数
egin{matrix}
ln f(x) equiv k ln g(x) & (mod x^n) \
f(x) = e^{k ln g(x)} & (mod x^n)
end{matrix}
这就解决了?别忘了常数项的问题,问题有两个:
- ([x^0]g(x) > 1),我们可以将常数项提出来,得到 (g'(x))(注意不是导数,这个就是 (g(x)) 的每项乘上常数项的逆元),那么问题变成了求 ((g'(x))^k c^k),(c) 代表常数项,就是在上面的过程进行完后每项乘上 (c^k) 即可;
- ([x^0]g(x) = 0),先位移到常数项不为 (0),即得到 (g'(x) = frac{g(x)}{x^d}),(d) 表示前缀连续 (0) 的个数,那么问题变成了求 ((g'(x))^k x^{kd}),就是上面的过程进行完后再位移 (kd) 位即可。
至此多项式乘方就完整地解决了。