1 条题解
-
0
自动搬运
来自洛谷,原作者为

Anguei
俺咕诶搬运于
2025-08-24 21:32:21,当前版本为作者最后更新于2018-11-01 16:55:01,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
感觉这道题现有的题解写的有点麻烦,解释不是非常清楚,所以这篇题解诞生了。
(本题解部分内容同步发表在 OI-Wiki)
前置知识:矩阵乘法
设 为 的矩阵, 为 的矩阵,设矩阵 为矩阵 与 的乘积,
其中矩阵 中的第 行第 列元素可以表示为:
如果没看懂上面的式子,没关系。通俗的讲,在矩阵乘法中,结果 矩阵的第 行第 列的数,就是由矩阵 第 行 个数与矩阵 第 列 个数分别相乘再相加得到的。
本题文字题解
斐波那契数列(Fibonacci Sequence)大家应该都非常的熟悉了。在斐波那契数列当中,,。
如果有一道题目让你求斐波那契数列第 项的值,最简单的方法莫过于直接递推了。但是如果 的范围达到了 级别,递推就不行了,稳 TLE。考虑矩阵加速递推。
设 表示一个 的矩阵 $\left[ \begin{array}{ccc}F_n & F_{n-1} \end{array}\right]$。我们希望根据 $Fib(n-1)=\left[ \begin{array}{ccc}F_{n-1} & F_{n-2} \end{array}\right]$ 推出 。
试推导一个矩阵 ,使 ,即 $\left[\begin{array}{ccc}F_{n-1} & F_{n-2}\end{array}\right] \times \text{base} = \left[ \begin{array}{ccc}F_n & F_{n-1} \end{array}\right]$。
怎么推呢?因为 ,所以 矩阵第一列应该是 ,这样在进行矩阵乘法运算的时候才能令 与 相加,从而得出 。同理,为了得出 ,矩阵 的第二列应该为 。
综上所述:$\text{base} = \left[\begin{array}{ccc} 1 & 1 \\ 1 & 0 \end{array}\right]$ ,原式化为 $\left[\begin{array}{ccc}F_{n-1} & F_{n-2}\end{array}\right] \times \left[\begin{array}{ccc} 1 & 1 \\ 1 & 0 \end{array}\right] = \left[ \begin{array}{ccc}F_n & F_{n-1} \end{array}\right]$
转化为代码,应该怎么求呢?
定义初始矩阵 $\text{ans} = \left[\begin{array}{ccc}F_2 & F_1\end{array}\right] = \left[\begin{array}{ccc}1 & 1\end{array}\right], \text{base} = \left[\begin{array}{ccc} 1 & 1 \\ 1 & 0 \end{array}\right]$。那么, 就等于 这个矩阵的第一行第一列元素,也就是 $\left[\begin{array}{ccc}1 & 1\end{array}\right] \times \left[\begin{array}{ccc} 1 & 1 \\ 1 & 0 \end{array}\right]^{n-2}$ 的第一行第一列元素。
注意,矩阵乘法不满足交换律,所以一定不能写成 $\left[\begin{array}{ccc} 1 & 1 \\ 1 & 0 \end{array}\right]^{n-2} \times \left[\begin{array}{ccc}1 & 1\end{array}\right]$ 的第一行第一列元素。另外,对于 的情况,直接输出 即可,不需要执行矩阵快速幂。
为什么要乘上 矩阵的 次方而不是 次方呢?因为 是不需要进行矩阵乘法就能求的。也就是说,如果只进行一次乘法,就已经求出 了。如果还不是很理解为什么幂是 ,建议手算一下。
下面是求斐波那契数列第 项对 取模的示例代码(核心部分)。
示例代码(核心部分)
const int mod = 1000000007; struct Matrix { int a[3][3]; Matrix() { memset(a, 0, sizeof a); } // 构造函数,矩阵初始化全零 Matrix operator*(const Matrix &b) const { Matrix res; for (int i = 1; i <= 2; ++i) for (int j = 1; j <= 2; ++j) for (int k = 1; k <= 2; ++k) res.a[i][j] = (res.a[i][j] + a[i][k] * b.a[k][j]) % mod; return res; } } ans, base; void init() { // 初始化 ans、base 矩阵 base.a[1][1] = base.a[1][2] = base.a[2][1] = 1; ans.a[1][1] = ans.a[1][2] = 1; } void qpow(int b) { // 求 while (b) { if (b & 1) ans = ans * base; base = base * base; b >>= 1; } } int main() { int n = read(); if (n <= 2) return puts("1"), 0; init(); qpow(n - 2); println(ans.a[1][1] % mod); }
如还有不懂的地方,可以在评论区提出来,或者直接私信我。
- 1
信息
- ID
- 928
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者