通过构建 2x2 状态转移矩阵并应用矩阵快速幂,在 O(log n) 时间内求斐波那契数列第 n 项模 10000 的值。
OJ: roj
题目 ID: 1645
难度:普及+/提高-
标签:数学矩阵快速幂斐波那契数列
创建: 2026-09-30 23:43
更新: 2026-10-07 13:50
形式化题目
给定若干个独立的查询,每个查询包含一个非负整数
正解
思路
对于
观察斐波那契数列的线性递推关系:
将状态向量表示为列向量
连续应用该矩阵变换,得到:
更进一步,利用数学归纳法可知:
因此,记转移矩阵
当
代码
Python 版:
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-03-31 09:00
# update_at: 2026-03-31 09:00
import sys
MOD = 10000 # 题目要求的模数
Matrix2x2 = tuple[int, int, int, int] # 行优先展平表示 2x2 矩阵 (m00, m01, m10, m11)
TRANS: Matrix2x2 = (1, 1, 1, 0) # 斐波那契状态转移矩阵 [[1, 1], [1, 0]]
I2: Matrix2x2 = (1, 0, 0, 1) # 2x2 单位矩阵
def mat_mul(a: Matrix2x2, b: Matrix2x2) -> Matrix2x2:
"""计算两个 2x2 矩阵的乘积模 10000。"""
return (
(a[0] * b[0] + a[1] * b[2]) % MOD,
(a[0] * b[1] + a[1] * b[3]) % MOD,
(a[2] * b[0] + a[3] * b[2]) % MOD,
(a[2] * b[1] + a[3] * b[3]) % MOD,
)
def mat_pow(base: Matrix2x2, exp: int) -> Matrix2x2:
"""计算 2x2 矩阵的 exp 次幂模 10000。"""
res = I2
while exp:
if exp & 1:
res = mat_mul(res, base)
base = mat_mul(base, base)
exp >>= 1
return res
def fib(n: int) -> int:
"""计算第 n 项斐波那契数模 10000。"""
if n == 0:
return 0
# [[F_{n+1}, F_n], [F_n, F_{n-1}]] = [[1, 1], [1, 0]]^n
# 所以 TRANS^n 的右上方元素(下标 1)即为 F_n
return mat_pow(TRANS, n)[1]
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
out: list[str] = []
for n in data:
if n == -1:
break
out.append(str(fib(n)))
print("\n".join(out))
if __name__ == "__main__":
solve()C++ 版(同一算法):
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-06 00:18
* update_at: 2026-10-06 00:18
*/
#include <cstdio>
typedef long long ll;
const ll MOD = 10000; // 题目要求对 10^4 取模
// 2x2 矩阵乘法:c = a * b (mod MOD),c 必须与 a、b 是不同的数组
void mat_mul(ll a[2][2], ll b[2][2], ll c[2][2]) {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
c[i][j] = 0;
for (int k = 0; k < 2; k++) {
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD;
}
}
}
}
// 计算斐波那契数列第 n 项模 MOD,用转移矩阵的快速幂,复杂度 O(log n)
ll fib(ll n) {
ll res[2][2] = {{1, 0}, {0, 1}}; // 单位矩阵,快速幂累积结果
ll base[2][2] = {{1, 1}, {1, 0}}; // 斐波那契转移矩阵 A
ll tmp[2][2]; // 矩阵乘法的临时结果
while (n > 0) {
if (n & 1) {
mat_mul(res, base, tmp);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
res[i][j] = tmp[i][j];
}
}
}
mat_mul(base, base, tmp);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
base[i][j] = tmp[i][j];
}
}
n >>= 1;
}
// A^n = [[F_{n+1}, F_n], [F_n, F_{n-1}]],右上角元素恰好是 F_n
return res[0][1];
}
int main() {
ll n;
while (scanf("%lld", &n) == 1) {
if (n == -1) {
break;
}
printf("%lld\n", fib(n));
}
return 0;
}复杂度
- 时间复杂度:设询问组数为
。对于每个询问 ,二进制位数为 。每次 矩阵乘法仅包含 次乘法和 次加法取模,耗时为常数 。因此单次计算复杂度为 ,整体时间复杂度为 。 - 空间复杂度:仅需常数级别的变量存储
矩阵,空间复杂度为 。
总结
线性递推式的核心优化手段是构建状态转移矩阵,将单步
在实现时,2 阶方阵状态较小,使用展平的元组或简单的常数循环即可高效完成乘法,每步对
图示解析
状态向量与转移矩阵关系如下:
flowchart LR
subgraph S0["基础向量 (k=0)"]
V0["[F1, F0]^T = [1, 0]^T"]
end
subgraph Mul["矩阵连续作用"]
A["乘以矩阵 A = [[1, 1], [1, 0]]"]
end
subgraph Sn["目标状态 (第 n 步)"]
Vn["[F(n+1), Fn]^T = A^n * [1, 0]^T"]
end
V0 -->|快速幂倍增 log n 次| Mul --> Vn
矩阵乘法规则(对模