1. 程式人生 > >矩陣優化遞推數列

矩陣優化遞推數列

Problem

求斐波那契數列第n項,輸出答案模10^9+7的值(n<2^63)

Prepare

(對於學過線代的大佬請跳至Solution)

我們先介紹一下矩陣,我們表現形式就是二維陣列。而矩陣相對於二維陣列不同的是具有乘法運算。

對於n*m的矩陣a和m*p的矩陣b,表示a*b的矩陣c是n*p的,其中矩陣c的第i行j列的元素滿足


舉個栗子,如下:


順便指一下,對於n*n的矩陣,唯一存在這個矩陣a,使得對於任意矩陣b,a*b=b,其中a的主對角線上所有數為1,其他數為0

如果還是看不懂,百度百科

Solution

對於斐波那契數列f(n)滿足f(n)=f(n-1)+f(n-2)(廢話!)

我們構造矩陣a,使得:


所以,我們可以通過計算a^n來得到答案,誒,這不是也要O(n)時間嗎?別忘了還有快速冪(kasumi),和普通整數的求冪大抵相差不了多少。

Code

#include<cstdio>
#include<cstring>
#define MOD 1000000007
using namespace std;
typedef long long LL;
struct Matrix {
    int n;
    LL a[2][2];
    Matrix():n(2){memset(a,0,sizeof(a));}
    Matrix operator*(const Matrix& mat){
        Matrix res;
        for(int i=0;i<n;i++)
            for(int j=0;j<mat.n;j++)
                for(int k=0;k<n;k++)
                    res.a[i][j]=(a[i][k]*mat.a[k][j]%MOD+res.a[i][j])%MOD;
        return res;
    }
    Matrix operator=(const Matrix& mat){memcpy(a,mat.a,sizeof(a));}
};
Matrix kasumi(Matrix d,LL k){
    Matrix ans;
    for(int i=0;i<ans.n;i++) ans.a[i][i]=1;
    while(k){
        if(k&1) ans=ans*d;
        d=d*d;
        k>>=1;
    }
    return ans;
}
int main(){
    Matrix d;
    LL n;
    scanf("%lld",&n);
    d.a[0][0]=d.a[0][1]=d.a[1][0]=1;
    printf("%lld",kasumi(d,n).a[1][0]);
}