斐波那契上斐波那契【矩阵快速幂】

数列满足

​G1​=2

G2​=3

Gi​=Gi−1​×Gi−2​ (i>2)​

求该数列 G 的前 n 项的乘积对 998244353 取模的结果。

很显然 gi​ 的质因子只有 2 和 3,设 gi​=2pi​⋅3qi​,问题转化为求 p,q。

由于 gi​=gi−1​⋅gi−2​,所以 pi​,qi​ 只能从 pi−1​,qi−1​ 和 pi−2​,qi−2​ 继承而来。

因为是相乘的关系,所以指数相加,也就是 pi​=pi−1​+pi−2​,qi​=qi−1​+qi−2​。

i=3:p=1,q=1

i=4:p=1,q=2

i=5:p=2,1=3.......

很明显满足p:i=3时开始的斐波那契,q满足i=2时开始的

然后斐波那契前n项的和又满足。。。。一个斐波那契

1 1 2 3 5 8

和:初始1 1+1=2 2+1=3 3+2=5 8。。。

所以sp=i从2开始的斐波那契,sq=i从1开始的斐波那契

知识点:快速幂

复制代码
#include <iostream>
using namespace std;

const int MOD = 998244353;

// 快速幂
long long qpow(long long a, long long b) {
    long long res = 1;
    a %= MOD;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    long long n;
    cin >> n;
    
    if (n == 1) {
        cout << 2 << endl;  // 只有 G1 = 2
        return 0;
    }
    if (n == 2) {
        cout << 6 << endl;  // 2 * 3 = 6
        return 0;
    }
    
    // 计算斐波那契数列模 MOD-1
    // 我们需要 Fib(n) 和 Fib(n+1) 模 MOD-1
    const int PHI = MOD - 1;  // 欧拉定理指数取模用
    
    // 初始化斐波那契
    long long f1 = 1, f2 = 1, fn = 1;  // Fib(1)=1, Fib(2)=1
    
    // 计算 Fib(n) 模 PHI
    if (n == 1) {
        fn = 1;
    } else if (n == 2) {
        fn = 1;
    } else {
        for (int i = 3; i <= n; i++) {
            fn = (f1 + f2) % PHI;
            f1 = f2;
            f2 = fn;
        }
    }
    long long Sp = fn;  // Sp(n) = Fib(n)
    
    // 重置计算 Fib(n+1)
    f1 = 1, f2 = 1;
    if (n + 1 == 1) {
        fn = 1;
    } else if (n + 1 == 2) {
        fn = 1;
    } else {
        for (int i = 3; i <= n + 1; i++) {
            fn = (f1 + f2) % PHI;
            f1 = f2;
            f2 = fn;
        }
    }
    long long Sq = (fn - 1 + PHI) % PHI;  // Sq(n) = Fib(n+1) - 1
    
    // 最终结果 = 2^Sp * 3^Sq mod MOD
    long long ans = qpow(2, Sp) * qpow(3, Sq) % MOD;
    cout << ans << endl;
    
    return 0;
}

此时我们如果注意到有条件n<10^18,那么直接求斐波那契就超时了,应该用矩阵快速幂

一、基本概念

1.1 快速幂

快速幂是一种快速计算 ab的算法,时间复杂度 O(log b):

复制代码
long long qpow(long long a, long long b) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

1.2 矩阵快速幂

矩阵快速幂是将快速幂的思想应用到矩阵乘法上,用于快速计算矩阵的 n 次幂:

  • 普通矩阵乘法:O(k³) 时间(k×k矩阵)

  • 矩阵快速幂:O(k³ log n) 时间计算 Mn

二、斐波那契数列的矩阵表示

2.1 递推式

斐波那契数列:F1​=1,F2​=1,Fn​=Fn−1​+Fn−2​

2.2 矩阵形式

我们可以写成:

复制代码

三、矩阵快速幂的实现

3.1 矩阵结构

复制代码
const int MOD = 998244353;

struct Matrix {
    long long m[2][2];
    
    Matrix() {
        m[0][0] = m[1][1] = 1;  // 单位矩阵
        m[0][1] = m[1][0] = 0;
    }
    
    Matrix(long long a, long long b, long long c, long long d) {
        m[0][0] = a; m[0][1] = b;
        m[1][0] = c; m[1][1] = d;
    }
};

3.2 矩阵乘法

复制代码
Matrix multiply(const Matrix& A, const Matrix& B) {
    Matrix C;
    C.m[0][0] = (A.m[0][0]*B.m[0][0] + A.m[0][1]*B.m[1][0]) % MOD;
    C.m[0][1] = (A.m[0][0]*B.m[0][1] + A.m[0][1]*B.m[1][1]) % MOD;
    C.m[1][0] = (A.m[1][0]*B.m[0][0] + A.m[1][1]*B.m[1][0]) % MOD;
    C.m[1][1] = (A.m[1][0]*B.m[0][1] + A.m[1][1]*B.m[1][1]) % MOD;
    return C;
}

3.3 矩阵快速幂

复制代码
Matrix mat_pow(Matrix base, long long n) {
    Matrix result;  // 初始为单位矩阵
    
    while (n > 0) {
        if (n & 1) {
            result = multiply(result, base);
        }
        base = multiply(base, base);
        n >>= 1;
    }
    return result;
}

四、计算斐波那契数列

4.1 完整实现

复制代码
long long fib(long long n) {
    if (n <= 0) return 0;
    if (n == 1 || n == 2) return 1;
    
    Matrix base(1, 1, 1, 0);  // 转移矩阵
    Matrix res = mat_pow(base, n - 2);  // 计算 M^{n-2}
    
    // F_n = res.m[0][0]*F_2 + res.m[0][1]*F_1
    return (res.m[0][0] + res.m[0][1]) % MOD;
}

#include <iostream>
using namespace std;

const int MOD = 998244353;

// 矩阵快速幂计算斐波那契数列的第 n 项
long long fib(long long n) {
    if (n <= 0) return 0;
    if (n == 1 || n == 2) return 1;
    n -= 2; // 从第三项开始
    long long a = 1, b = 1, c, d;
    long long aa = 1, bb = 1, cc, dd;
    // 矩阵快速幂:初始矩阵为 {{1,1},{1,0}} 的 n 次幂
    long long res[2][2] = {{1,0},{0,1}}; // 单位矩阵
    long long base[2][2] = {{1,1},{1,0}};
    while (n > 0) {
        if (n & 1) {
            // res = res * base
            long long t00 = (res[0][0]*base[0][0] + res[0][1]*base[1][0]) % (MOD-1);
            long long t01 = (res[0][0]*base[0][1] + res[0][1]*base[1][1]) % (MOD-1);
            long long t10 = (res[1][0]*base[0][0] + res[1][1]*base[1][0]) % (MOD-1);
            long long t11 = (res[1][0]*base[0][1] + res[1][1]*base[1][1]) % (MOD-1);
            res[0][0]=t00; res[0][1]=t01; res[1][0]=t10; res[1][1]=t11;
        }
        // base = base * base
        long long t00 = (base[0][0]*base[0][0] + base[0][1]*base[1][0]) % (MOD-1);
        long long t01 = (base[0][0]*base[0][1] + base[0][1]*base[1][1]) % (MOD-1);
        long long t10 = (base[1][0]*base[0][0] + base[1][1]*base[1][0]) % (MOD-1);
        long long t11 = (base[1][0]*base[0][1] + base[1][1]*base[1][1]) % (MOD-1);
        base[0][0]=t00; base[0][1]=t01; base[1][0]=t10; base[1][1]=t11;
        n >>= 1;
    }
    // 初始 F1=1, F2=1, 乘以矩阵 {{F2,F1},{F1,F0}} 得到 F_{n+2}
    // 我们只需要 F_{n+2} 中的一项即可
    return (res[0][0] + res[0][1]) % (MOD-1);
}

// 快速幂
long long qpow(long long a, long long b) {
    long long res = 1;
    a %= MOD;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    long long n;
    cin >> n;
    if (n == 1) {
        // 只有 G1 = 2
        cout << 2 << endl;
        return 0;
    }
    if (n == 2) {
        // G1 * G2 = 2 * 3 = 6
        cout << 6 << endl;
        return 0;
    }
  
    long long Sp = fib(n);  // fib 函数返回的是模 MOD-1 的结果
    // 计算 Fib(n+1) mod (MOD-1)
    long long Sq = fib(n+1);
    Sq = (Sq - 1 + (MOD-1)) % (MOD-1);
    
    // 最终结果 = 2^Sp * 3^Sq mod MOD
    long long ans = qpow(2, Sp) * qpow(3, Sq) % MOD;
    cout << ans << endl;
    
    return 0;
}
相关推荐
早睡的叶子2 小时前
onnx模型数据结构分析,用于解析onnx模型
数据结构
@atweiwei2 小时前
Go语言面试篇数据结构底层原理精讲(下)
数据结构·面试·golang
CHANG_THE_WORLD2 小时前
模拟解析:宽度数组 `[1,2,1]`,10个条目的 XRef 流
java·前端·算法
lixinnnn.2 小时前
多源BFS:矩阵距离
算法·宽度优先
CHANG_THE_WORLD2 小时前
PDFium 处理通用 `W` 数组的方式
数据结构·算法
派大星~课堂2 小时前
【力扣-94.二叉树的中序遍历】Python笔记
笔记·python·leetcode
lixinnnn.2 小时前
多源BFS:刺杀大使
算法·宽度优先
ZhiqianXia3 小时前
PyTorch 学习笔记(10) : PyTorch torch.library
pytorch·笔记·学习
小陈phd3 小时前
多模态大模型学习笔记(三十一)—— 基于CCT(Compact Convolutional Transformers)实现中文车牌数据集微调
笔记·学习