要不是考到了,我还没发现这玩意我不是很会……
# 前置
- 多项式取模;
- 矩阵快速幂。
# 常系数齐次线性递推
描述的是这么一个问题,给定数列 $c_1,c_2,\dots,c_k$ 以及数列 $f$ 的前 $k$ 项 $f_0,f_1,\dots,f_{k-1}$,已知 $f$ 有如下递推公式:
求 $f_n \bmod 998244353$,其中 $n$ 可以很大,$k$ 是 $10^5$ 左右的数。
- 常系数:递推式的系数 $c_i$ 均为常数;
- 齐次:这意味着递推式没有常数项(如果有常数项就别想了);
- 线性:$f_i$ 的指数都为 $1$。
# 算法原理
对于这种系数为常数的问题,我们有一个通用的解法 —— 矩阵快速幂:
记最后那个 $k\times k$ 的转移方阵为 $A$,初始列向量为 $St$。常规的计算方法即计算
复杂度 $\mathcal O(k^3\log n)$,主要的瓶颈在于矩阵乘法。
下面要介绍的算法给出了这样一种构造,其中 $\{c_i\}$ 是针对 $A^n$(注意不仅与 $A$ 有关,还与指数 $n$ 有关)构造的数列 ——
目前看来好像没有什么茄子用,仍然需要计算矩乘。但是我们真的需要 $A^n\times St$ 这整个向量吗?实际上我们只需要 $f_n$,即 $A^n\times St$ 的第一项。
再看看这个式子就可以发现它的用处了:
$A^i\times St$ 的实际意义是将 $St$ 转移 $i$ 次。所以 $(A^i\times St)_1=f_i$,也即
这样就免去了矩乘。
这就是这个算法的全部内容了?还剩下一个问题,$\{c_i\}$ 怎么构造?
定义函数 $C(x)$ 如下,则要求 $C(A)=A^n$。
接下来是一些魔法……如果我们有函数 $F(x)$ 满足
且有 $G(x)$ 满足:
易得
利用多项式取模对快速幂稍加改造就可以计算 $C(x)$。
「稍 加 改 造」
说起来倒也简单,把多项式 $x$ 拿来做快速幂,对 $F(x)$ 取模。
然后我们发现又需要构造 $F(x)$……如果要对一个一般的方阵求 $F(A)=0$ 那确实很难,但常系数齐次线性递推的转移矩阵 $A$ 因为它的结构特殊,有一个简洁的构造:
- $f_k=1$;
- $f_{i}=c_{k-i}$($0\le i\lt k$)。
至于为什么这样构造,就涉及到矩阵的特征向量的内容,和这个算法本身没有太紧的关联。有兴趣的读者可以参考 shadowice1984 的洛谷博客。
THE END
Thanks for reading!
我也曾 隐约想过 从这世界逃离
因为有无数次 和最优解失之交臂
那时耀眼的自己 定不会轻易容许
骄傲变得同墙角霉菌 不差毫厘
——《我也曾想过一了百了(中文填词)》 By 洛天依
> Link 我也曾想过一了百了 - Bilibili