题目链接
题目描述
给定经典的斐波那契数列 ,其定义如下:
现给定两个正整数 与
(
),请你计算
并对
取模后输出结果。
解题思路
本题要求计算两个斐波那契数 和
的最大公约数(GCD)。由于
和
可能非常大,直接计算
和
是不可行的。
1. 关键性质
解决本题的核心在于利用斐波那契数列的一个重要数论性质:
这个定理指出,两个斐波那契数的最大公约数,等于它们下标的最大公约数所对应的那个斐波那契数。
这个优美的性质将问题极大地简化了。我们不再需要处理两个庞大的斐波那契数,而是可以先计算出下标的 ,然后再求对应的斐波那契数。
2. 问题转化
根据上述性质,原问题 就等价于计算
。
设
,我们的新目标是计算
。
首先,我们可以用欧几里得算法(辗转相除法)快速地计算出 ,其时间复杂度为
。
3. 求解 
计算出 之后,问题就变成了“求解斐波那契数列第
项模
的值”。由于
的值仍然可能非常大,因此需要使用矩阵快速幂算法。
这与 PEEK149 斐波那契数列
的解法完全一致:
- 构造转移矩阵: 斐波那契数列的递推关系可以表示为矩阵形式:
- 矩阵快速幂: 我们可以通过计算转移矩阵的
次幂,来从初始状态
递推到
:
其中。这个幂次计算可以在
时间内完成。
4. 算法流程总结
- 读入
。
- 使用欧几里得算法计算
。
- 如果
,结果为
。
- 否则,使用矩阵快速幂计算
,得到结果矩阵
。
- 最终答案为
。
代码
#include <iostream>
#include <vector>
#include <numeric>
using namespace std;
const long long MOD = 1000000007;
struct Matrix {
long long mat[2][2];
Matrix() {
mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
}
};
Matrix multiply(Matrix A, Matrix B) {
Matrix C;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
C.mat[i][j] = (C.mat[i][j] + A.mat[i][k] * B.mat[k][j]) % MOD;
}
}
}
return C;
}
Matrix power(Matrix A, long long exp) {
Matrix result;
result.mat[0][0] = result.mat[1][1] = 1; // 单位矩阵
while (exp > 0) {
if (exp % 2 == 1) result = multiply(result, A);
A = multiply(A, A);
exp /= 2;
}
return result;
}
long long gcd(long long a, long long b) {
return b == 0 ? a : gcd(b, a % b);
}
int main() {
long long n, m;
while (cin >> n >> m) {
long long g = gcd(n, m);
if (g <= 2) {
cout << 1 << endl;
continue;
}
Matrix T;
T.mat[0][0] = 1; T.mat[0][1] = 1;
T.mat[1][0] = 1; T.mat[1][1] = 0;
T = power(T, g - 2);
long long ans = (T.mat[0][0] * 1 + T.mat[0][1] * 1) % MOD;
cout << ans << endl;
}
return 0;
}
import java.util.Scanner;
public class Main {
static final long MOD = 1000000007;
static long[][] multiply(long[][] a, long[][] b) {
long[][] c = new long[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD;
}
}
}
return c;
}
static long[][] power(long[][] a, long exp) {
long[][] res = {{1, 0}, {0, 1}}; // 单位矩阵
while (exp > 0) {
if ((exp & 1) == 1) res = multiply(res, a);
a = multiply(a, a);
exp >>= 1;
}
return res;
}
static long gcd(long a, long b) {
return b == 0 ? a : gcd(b, a % b);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNextLong()) {
long n = sc.nextLong();
long m = sc.nextLong();
long g = gcd(n, m);
if (g <= 2) {
System.out.println(1);
continue;
}
long[][] T = {{1, 1}, {1, 0}};
T = power(T, g - 2);
long ans = (T[0][0] * 1 + T[0][1] * 1) % MOD;
System.out.println(ans);
}
}
}
import sys
MOD = 1000000007
def gcd(a, b):
return a if b == 0 else gcd(b, a % b)
def multiply(a, b):
c = [[0, 0], [0, 0]]
for i in range(2):
for j in range(2):
for k in range(2):
c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD
return c
def power(a, exp):
res = [[1, 0], [0, 1]] # 单位矩阵
while exp > 0:
if exp % 2 == 1:
res = multiply(res, a)
a = multiply(a, a)
exp //= 2
return res
for line in sys.stdin:
if not line.strip():
continue
n, m = map(int, line.split())
g = gcd(n, m)
if g <= 2:
print(1)
continue
T = [[1, 1], [1, 0]]
T = power(T, g - 2)
ans = (T[0][0] * 1 + T[0][1] * 1) % MOD
print(ans)
算法及复杂度
- 算法: 欧几里得算法 + 矩阵快速幂
- 时间复杂度: 计算
的时间复杂度为
。计算
的矩阵快速幂时间复杂度为
。因此,总时间复杂度为
。
- 空间复杂度: 算法只需要常数个变量和矩阵来存储中间结果,因此空间复杂度为
。