Acwing205. 斐波那契

文章目录

  • 题意
  • 思路
  • 代码

题意

求斐波那契数列的第n项,但是这里的 0 ≤ n ≤ 1 0 9 0 \le n \le 10^9 0n109

思路

首先因为这儿的n很大,所以我们不能够直接像以前那样直接递推求出第n项,但是我们知道这道题,肯定是需要递推来求。所以这儿我们将引入矩阵快速幂的方法来进行求解。

我们都知道斐波那契数列的性质 f n = f n − 1 + f n − 2 , f n + 1 = f n + f n − 1    a n d    n ≥ 3 f_n = f_{n-1}+f_{n-2},f_{n+1} = f_{n}+f_{n-1} \ \ and \ \ n \ge 3 fn=fn1+fn2fn+1=fn+fn1  and  n3。那么到这儿我们如果能构造出一个矩阵A使得 F n ∗ A = F n + 1 F_n*A = F_{n+1} FnA=Fn+1,那么如果我们得知矩阵 F 1 F_1 F1那么如果我们要求 F N F_N FN是不是只需要用 ( ( F 1 ∗ A ) ∗ A ) ∗ A . . . < 1 > ((F_1*A)*A)*A...<1> ((F1A)A)A...<1>这样一直乘下去乘上n-1次那么我们将能够求出 F n F_n Fn了,又因为对于矩阵乘法是具有结合律(这也是我们为什么能够进行快速幂的原因)那么我们就能够将<1>式转化成 F 1 ∗ A n − 1 F_1*A^{n-1} F1An1而对于 A n − 1 A^{n-1} An1如果我们能够快速求出,是不是就能够大大的节约时间了。那么对于以前来说我们快速求 a x a^x ax是不是用到了快速幂能够在 O ( l o g n ) O(log_n) O(logn)的时间求出。对于矩阵我们可以用同样的方法实现快速求出。

那么说了这么多,但是最关键的是我的 F 1 F_1 F1 A A A这两个矩阵怎么去构造出来呢?这也是矩阵快速幂的难点。

对于本题来说我们可以根据斐波那契数列的性质去构造矩阵 F n F_n Fn
F n = [ f n − 2 , f n − 1 ] F_n= \begin{bmatrix} f_{n-2},f_{n-1} \end{bmatrix} Fn=[fn2,fn1]
因为 f n = f n − 1 + f n − 2 f_n = f_{n-1}+f_{n-2} fn=fn1+fn2

那么同理对于 F n + 1 F_{n+1} Fn+1
F n + 1 = [ f n − 1 , f n ] F_n+1= \begin{bmatrix} f_{n-1},f_{n} \end{bmatrix} Fn+1=[fn1,fn]
那么前面我们说了 F n ∗ A = F n + 1 F_n*A=F_{n+1} FnA=Fn+1那么根据矩阵乘法的性质矩阵A的大小是2*2。我们设矩阵A
A = [ a 1 a 3 a 2 a 4 ] A = \begin{bmatrix} a_1 & a_3\\ a_2& a_4 \end{bmatrix} A=[a1a2a3a4]

那么我们手算一下 F n ∗ A = F n + 1 F_n*A=F_{n+1} FnA=Fn+1
那么就是
f n − 2 ∗ a 1 + f n − 1 ∗ a 2 = f n − 1 f n − 2 ∗ a 3 + f n − 1 ∗ a 4 = f n f_{n-2}*a_1+f_{n-1}*a_2= f_{n-1} \\ f_{n-2}*a_3+f_{n-1}*a_4= f_{n} fn2a1+fn1a2=fn1fn2a3+fn1a4=fn

显然我们可以得知, a 1 = 0 , a 2 = 1 , a 3 = 1 , a 4 = 1 a_1 = 0, a_2 = 1, a_3 = 1, a_4 = 1 a1=0,a2=1,a3=1,a4=1,所以矩阵A
A = [ 0 1 1 1 ] A = \begin{bmatrix} 0 & 1\\ 1& 1 \end{bmatrix} A=[0111]

那么现在我们的A矩阵就求出来了。我们斐波那契数列的性质是当下标大于等于3的时候才能推出递推式。那么对于本题我们的 F 1 = [ f 1 , f 2 ] = [ 1 , 1 ] F_1 = [f1, f2] = [1, 1] F1=[f1,f2]=[1,1]我们应该是从第三项开始推起的,那么对于我上述的 A n − 1 A^{n-1} An1这儿应该是 A n − 3 A^{n-3} An3

PS:对于矩阵快速幂的求法,大家可以参照龟速乘的做法

代码

#include

#define IOS ios::sync_with_stdio(false);cin.tie(nullptr)
#define int long long 
#define endl "\n"

using namespace std;

const int N = 2, mod = 10000;

int n;

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

void mul(int c[][N], int b[][N], int a[][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] + b[i][k] * a[k][j]) % mod;
            }
        }
    }
    
    memcpy(c, tmp, sizeof tmp);
}

signed main()
{
    while(cin >> n)
    {
        if(n == -1) break;
        
        if(n == 0)
        {
            cout << 0 << endl;
            continue;
        }
        if(n == 1 || n == 2)
        {
            cout << 1 << endl;
            continue;
        }
        
        int f[N]={1, 1};
        int a[N][N] = {
            {0, 1},
            {1, 1}
        };
        
        n -= 3;
        while(n)
        {
            if(n & 1) mul(f, f, a);
            mul(a, a, a);
            n >>= 1;
        }
        
        cout << (f[0]+f[1]) % mod << endl;
    }
    return 0;
}

你可能感兴趣的:(算法竞赛进阶指南-数学,数学,算法)