多项式多点求值
传统做法,原理是:
\[\begin{aligned} x^n &\equiv ax^{n-1}\pmod {x-a}\\ &\equiv a^2x^{n-1}\pmod {x-a}\\ &\dots\\ &\equiv a^n\pmod {x-a} \end{aligned} \]所以对于要求值的多项式 \(F(x)\) 和给定的点 \(x_1,x_2,\dots x_m\) ,我们就相当于求 \(F(x)\bmod (x-x_1),\dots,F(x)\bmod (x-x_m)\) 。
直接求当然不行,但是如果我们求 \(F(x)\bmod (x-a)\) ,我们先将 \(F(x)\) 模上一个包含 \((x-a)\) 因式的多项式 \(P(x)\) ,则 \((F(x)\bmod P(x))\equiv F(x)\pmod {x-a}\) 。
这提示我们可以逐步降低 \(F\) 的次数来优化复杂度。采用分治策略,记 \(P_{l,r}(x)=\prod_{i=l}^r(x-x_i)\) ,且 \(M=\lfloor\frac{l+r}2\rfloor\) 。如果我们得到了 \(F(x)\bmod P_{l,r}(x)\) ,我们可以让余式分别模 \(P_{l,M}\) 和 \(P_{M+1,r}\) ,从而可以继续递归。
\(P\) 需要最初分治求并且存下来。
可以得到时间复杂度为 \(O(m\log_2^2m)\) ,由于频繁使用多项式取模,该算法拥有较大的常数。
船新做法:[1]
普通的和卷积为 \(h_k=\sum_{i+j=k}f_ig_j\) ,我们用 \(H=F\cdot G\) 或 \(H(x)=F(x)\cdot G(x)\) 来表示。
现在我们定义差卷积为 \(h_k=\sum_{i-j=k}f_ig_j\) ,我们用 \(H=F\times G\) 或 \(H(x)=F(x)\times G(x)\) 来表示[2]。
这个做法的原理是:
\[[x^0]F(x)\times \frac{1}{1-ax}=F(a) \]自行展开即可得知。
除此之外,差卷积还有重要的性质:
\[F\times (G\cdot H)=F\times G\times H \]按照定义展开并整理即可得证。
因此,我们就相当于求 \([x^0]F(x)\times \frac{1}{1-x_1x},\dots,[x^0]F(x)\times \frac{1}{1-x_mx}\) 。
同样采取分治策略。记 \(Q_{l,r}(x)=\prod_{i=l}^r(1-x_ix)\) ,且 \(M=\lfloor\frac{l+r}2\rfloor\) 。如果我们得到了 \(F(x)\times \frac{1}{Q_{l,r}(x)}\) ,我们就乘上 \(Q_{M+1,r}(x)\) 并进入左子区间递归;右子区间类似。
由于我们最终只需要常数项的值,因此我们可以控制多项式长度为区间长度,这样就保证了时间复杂度。
\(Q\) 同样需要最初分治求并且存下来。递归根部的 \(F(x)\times \frac{1}{Q_{1,m}(x)}\) 需要求逆。
那么时间复杂度为 \(O(m\log_2^2m)\) ,不过常数会小一些,并且也会好写一些。
多项式快速插值
传统做法,原理是拉格朗日插值:
\[f(x)=\sum_{i=1}^n\frac{y_i\prod_{j\not=i}(x-x_j)}{\prod_{j\not=i}(x_i-x_j)} \]我们只需要快速展开它即可。
将式子变形:
\[f(x)=\sum_{i=1}^n\left(\frac{y_i}{\prod_{j\not=i}(x_i-x_j)}\cdot\prod_{j\not=i}(x-x_j)\right) \]首先处理 \(\prod_{j\not=i}(x_i-x_j)\) 。设 \(g(x)=\prod_{i=1}^n(x-x_i)\) ,则它相当于 \(h_i(x)=\frac{g(x)}{x-x_i}\) 在 \(x_i\) 处的值。
可以发现 \(h_i(x)\) 在 \(x_i\) 有定义,而 \(\frac{g(x_i)}{x-x_i}\) 却没有定义。这就意味着后者的极限即为所需:
\[\lim_{x\rightarrow x_i}\frac{g(x)}{x-x_i}=\lim_{x\rightarrow x_i}\frac{g(x)-g(x_i)}{x-x_i}=g'(x_i) \]所以我们可以分治求出 \(g(x)\) 然后多点求值得到 \(\prod_{j\not=i}(x_i-x_j)\) 。
接下来求解 \(f(x)\) 继续采用分治。设 \(f_{l,r}(x)\) 为 \([l,r]\) 的结果:
\[f_{l,r}(x)=\sum_{i=l}^r\left(\frac{y_i}{\prod_{j\not=i}(x_i-x_j)}\cdot\prod_{l\le j\le r,j\not=i}(x-x_j)\right) \]设分治的划分点为 \(M\) ,那么可以得到:
\[f_{l,r}(x)=f_{l,M}(x)\cdot\prod_{i=M+1}^r(x-x_i)+f_{M+1,r}(x)\prod_{i=l}^M(x-x_i) \]边界为 \(f_{i,i}(x)=\frac{y_i}{\prod_{j\not=i}(x_i-x_j)}\) 。
这个做法的复杂度是 \(O(n\log_2^2n)\) ,常数也就见仁见智吧。
船新做法:还没有出现
常系数齐次线性递推
传统做法:
我们可以很容易地构造出转移矩阵 \(T\) 。
设初值的向量为 \(\bold{A}\) ,我们可以知道有:
\[a_n=(\bold{A}\times T^n)_0 \]根据递推式,当整数 \(n\ge k\) 时有:
\[(\bold{A}\times T^n)_0=\sum_{i=1}^kf_i(\bold{A}\times T^{n-i})_0 \]尝试将 \(()_0\) 去掉,也就是要说明对于整数 \(m\in[0,k)\) 都有:
\[(\bold A\times T^n)_m=\sum_{i=1}^kf_i(\bold A\times T^{n-i})_m \]显然有:
\[(\bold A\times T^n)_m=(\bold A\times T^{n+m})_0=a_{n+m} \]所以可以得到:
\[\bold A\times T^{n}=\sum_{i=1}^kf_i\times \bold A\times T^{n-i} \]考虑 \(\bold A\) 不为 0 向量的时候,所以:
\[T^n=\sum_{i=1}^kf_iT^{n-i} \]这是一个关于 \(T\) 的等式,也告诉我们 \(T^n\) 可以被表示为较低次项的多项式。所以我们可以对其最高次项频繁进行展开,直到多项式次数降为 \(k-1\) ;这个过程相当于是进行多项式取模:
\[T^n\bmod{\left(T^k-\sum_{i=0}^{k-1}f_{k-i}T^i\right)} \]因此,如果我们得到取模的系数,我们就可以将初值带入得到 \(a_n\) 的值。
所以我们需要对多项式做快速幂,模式就为 \(T^k-\sum_{i=0}^{k-1}f_{k-i}T^i\) 。
时间复杂度是 \(O(k\log_2n\log_2k)\) 。
背后的严格证明就略过吧
船新做法:[3]
常系数齐次线性递推有其一般形式。设 \(A(x)\) 为数列 \(\{a_n\}\) 的生成函数,则:
\[A(x)=\frac{P(x)}{Q(x)} \]其中 \(Q(x)\) 与递推式 \(\{f_k\}\) 的生成函数 \(F(x)\) 相关,即 \(Q(x)=1-F(x)\) ; \(P(x)\) 与 \(A(x),Q(x)\) 均相关。
来源:
将 \(a_n\) 的递推式改一改得到生成函数形式:
\[\begin{aligned} a_nx^n&=\sum_{i=1}^kf_ix^i\cdot a_{n-i}x^{n-i}\\ \Rightarrow A(x)&=F(x)A(x) \end{aligned} \]但是这里的结果并不完整,因为当 \(n\ge k\) 的时候递推式才是有效的,当 \(n<k\) 的时候,需要进行一些修补。
怎么修?很好办,减去不对的再加回对的就可以了,所以有:
\[P(x)\equiv (1-F(x))A(x)\pmod{x^{k}} \]
根据这个形式我们可以得到什么?
进行一些充满想象力的变换:
设 \(V(x)=Q(x)Q(-x)\) 。注意到 \(V(x)=V(-x)\) ,所以 \(V(x)\) 没有奇数项。
这意味着我们进行更深入的变化。设 \(U(x^2)=V(x)\) ,那么有:
\[A(x)=\frac{G_0(x^2)}{U(x^2)}+x\frac{G_1(x^2)}{U(x^2)} \]其中 \(G_0(x^2)\) 为 \(P(x)Q(-x)\) 的偶数项对应的多项式, \(xG_1(x^2)\) 为 \(P(x)Q(-x)\) 的奇数项对应的多项式。
由于我们只需要求 \([x^n]A(x)\) ,所以我们可以舍弃一半的多项式,也就相当于是将多项式长度减半。最终我们只需要求某个分式的 \([x^0]\) ,就可以直接计算了。
这个算法的复杂度也是 \(O(k\log_2n\log_2k)\) ,但是显然它会好写一些,常数也会小一些。
据测试,除开各种复杂的优化,朴素的该算法的常数也仅有传统做法的 \(\frac 1 3\) 左右。