题目链接

PEEK151 斐波那契公约数

题目描述

给定经典的斐波那契数列 ,其定义如下:

现给定两个正整数 ),请你计算 并对 取模后输出结果。

解题思路

本题要求计算两个斐波那契数 的最大公约数(GCD)。由于 可能非常大,直接计算 是不可行的。

1. 关键性质

解决本题的核心在于利用斐波那契数列的一个重要数论性质:

这个定理指出,两个斐波那契数的最大公约数,等于它们下标的最大公约数所对应的那个斐波那契数。

这个优美的性质将问题极大地简化了。我们不再需要处理两个庞大的斐波那契数,而是可以先计算出下标的 ,然后再求对应的斐波那契数。

2. 问题转化

根据上述性质,原问题 就等价于计算 。 设 ,我们的新目标是计算

首先,我们可以用欧几里得算法(辗转相除法)快速地计算出 ,其时间复杂度为

3. 求解

计算出 之后,问题就变成了“求解斐波那契数列第 项模 的值”。由于 的值仍然可能非常大,因此需要使用矩阵快速幂算法。

这与 PEEK149 斐波那契数列 的解法完全一致:

  1. 构造转移矩阵: 斐波那契数列的递推关系可以表示为矩阵形式:
  2. 矩阵快速幂: 我们可以通过计算转移矩阵的 次幂,来从初始状态 递推到
    其中 。这个幂次计算可以在 时间内完成。

4. 算法流程总结

  1. 读入
  2. 使用欧几里得算法计算
  3. 如果 ,结果为
  4. 否则,使用矩阵快速幂计算 ,得到结果矩阵
  5. 最终答案为

代码

#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)

算法及复杂度

  • 算法: 欧几里得算法 + 矩阵快速幂
  • 时间复杂度: 计算 的时间复杂度为 。计算 的矩阵快速幂时间复杂度为 。因此,总时间复杂度为
  • 空间复杂度: 算法只需要常数个变量和矩阵来存储中间结果,因此空间复杂度为