题目-Fibonacci 数列(矩阵乘法)

在这里插入图片描述

问题分析

假设求第 n n n项, 相当于求矩阵的 A n A^ n An可以用快速幂协助求解
但是还是要求前 n n n项和 S n S_n Sn, 需要构造矩阵

首先考虑第求 n n n项的情况
F n = [ f n , f n + 1 ] F_n = [f_n, f_{n + 1}] Fn=[fn,fn+1]
F n + 1 = [ f n + 1 , f n + 2 ] F_{n + 1} = [f_{n + 1}, f_{n + 2}] Fn+1=[fn+1,fn+2]

推导之后发现

[ f n + 1 , f n + 2 ] = [ f n , f n + 1 ] × [ 0 1 1 1 ] [f_{n + 1}, f_{n + 2}] = [f_n, f_{n + 1}] \times \begin{bmatrix} 0 & 1\\ 1 & 1 \end{bmatrix} [fn+1,fn+2]=[fn,fn+1]×[0111]

那么矩阵 A = [ 0 1 1 1 ] A = \begin{bmatrix} 0 & 1\\ 1 & 1 \end{bmatrix} A=[0111], 第 n n n项 = F 0 × A n F_0 \times A ^ n F0×An, 因为矩阵具有结合律, 可以用快速幂计算

但是题目要求的是 S n S_n Sn, 因此需要重新设计矩阵

F n = [ f n , f n + 1 , S n ] ,    F n + 1 = [ f n + 1 , f n + 2 , S n + 1 ] F_n = [f_n, f_{n + 1}, S_n], \; F_{n + 1} = [f_{n + 1}, f_{n + 2}, S_{n + 1}] Fn=[fn,fn+1,Sn],Fn+1=[fn+1,fn+2,Sn+1]

得到等式
[ f n + 1 , f n + 2 , S n + 1 ] = [ f n , f n + 1 , S n ] × [ 0 1 0 1 1 1 0 0 1 ] [f_{n + 1}, f_{n + 2}, S_{n + 1}] = [f_n, f_{n + 1}, S_n] \times \begin{bmatrix} 0& 1 & 0\\ 1& 1 & 1\\ 0& 0 & 1 \end{bmatrix} [fn+1,fn+2,Sn+1]=[fn,fn+1,Sn]× 010110011
也就是 A = [ 0 1 0 1 1 1 0 0 1 ] A = \begin{bmatrix} 0& 1 & 0\\ 1& 1 & 1\\ 0& 0 & 1 \end{bmatrix} A= 010110011

实际计算的答案就是 F n = F 0 × A n F_n = F_0 \times A^ n Fn=F0×An, 快速幂实现矩阵乘法

算法步骤

主要实现快速幂的乘积使用矩阵乘法计算即可
因为 n ≤ 2 × 1 0 9 n \le 2 \times 10 ^ 9 n2×109, 快速幂算法时间复杂度 O ( log ⁡ n ) O(\log n) O(logn), 可以通过

代码实现

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
const int N = 3;

int n, m;

void mul(int c[], int a[], int b[][N]) {
   
    int tmp[N] = {
   0};
    for (int i = 0; i < N; ++i) {
   
        for (int j = 0; j < N; ++j) {
   
            tmp[i] = (tmp[i] + (LL) a[j] * b[j][i] % m) % m;
        }
    }
    memcpy(c, tmp, sizeof tmp);
}

void mul(int c[][N], int a[][N], int b[][N]) {
   
    int tmp[N][N] = {
   0};
    for (int i = 0; i < N; ++i) {
   
        for (int j = 0; j < N; ++j) {
   
            for (int k = 0; k < N; ++k) {
   
                tmp[i][j] = (tmp[i][j] + (LL) a[i][k] * b[k][j] % m) % m;
            }
        }
    }
    memcpy(c, tmp, sizeof tmp);
}

int main() {
   
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    int f1[N] = {
   1, 1, 1};
    int A[N][N]= {
   {
   0, 1, 0},
                  {
   1, 1, 1},
                  {
   0, 0, 1}};

    n--;
    while (n) {
   
        if (n & 1) mul(f1, f1, A);
        mul(A, A, A);
        n >>= 1;
    }

    cout << f1[2] % m << '\n';

    return 0;
}