1. 程式人生 > >【洛谷P1962 斐波那契數列】矩陣快速冪+數學推導

【洛谷P1962 斐波那契數列】矩陣快速冪+數學推導

公式 lin esp inline i++ out cin def res

來提供兩個正確的做法:

  • 斐波那契數列雙倍項的做法(附加證明)
  • 矩陣快速冪

一、雙倍項做法

在偶然之中,在百度中翻到了有關於斐波那契數列的詞條(傳送門),那麽我們可以發現一個這個規律$ \frac{F_{2n}}{F_{n}}=F_{n-1}+F_{n+1} $,那麽我就想到了是不是可以用這個公式實現類似於快速冪之類的東西:power(n,m)=power(n*n,m/2) m mod 2=0 power(n,m)=power(n*n,m/2)*n m mod 2=1

快速冪這個東西,是分成偶數情況和奇數情況,所以我們只是知道偶數想的計算公式,所以我們接下來要推導一下奇數項的遞歸式
\[ F_{2n}=F_{n}\times(F_{n-1}+F_{n+1})\]


\[ F_{2n+2}=F_{n+1}\times(F_{n}+F_{n+2})\]
那麽我們就是要從\(F_{2n}\)\(F_{2n+2}\)推導求出\(F_{2n+1}\)
\[ F_{2n+1}=F_{2n+2}-F_{2n} \]
\[ F_{2n+1}=F_{n+1}\times(F_{n}+F_{n+2})-F_{n}*(F_{n-1}+F_{n+1}) \]
\[ F_{2n+1}=F_{n+1}\times F_{n}+F_{n+1}\times F_{n+2} - F_{n}\times F_{n-1}-F_{n}\times F_{n+1}\]
\[ F_{2n+1}=F_{n+1}\times F_{n+2}-F_{n}\times F_{n-1}\]

\[ F_{2n+1}=F_{n+1}\times(F_{n+1}+F_{n})-F_{n}\times(F_{n+1}-F_{n})\]
\[ F_{2n+1}={F_{n+1}}^2+{F_{n}}^2 \]
以上就是我們對於這個公式的推導
那麽我們就得到了

F[2n] = F[n+1]2 - F[n-1]2 = (2F[n-1] + F[n]) · F[n]

F[2n+1] = F[n+1]2 + F[n]2

那麽,我們在寫一個map,那麽就可以不用全部都遞歸到底了,優化一下。
用map映射一下大數,映射到我們的答案上。

#include <bits/stdc++.h>
using namespace std;
const int Mod=1e9+7;//mod數
long long n;
map<long long,long long> ma;//搞映射
inline long long work(long long x){
    if(x==1||x==0)return 1;//邊界
    if(ma.count(x))return ma[x];//count如果是返回1那麽就是這個答案已經在map中映射過了,0就是沒有
    long long res=0,t=x/2;
    if(x&1) res=work(t)*(work(t-1)+work(t+1))%Mod;//公式2
    else res=work(t)*work(t)%Mod+work(t-1)*work(t-1)%Mod;//公式1
    return ma[x]=res;
}
int main() {//主程序
    cin>>n;
    long long res=work(n-1)%Mod;
    cout<<res<<endl;
    return 0;
}

註:這個程序的復雜度是也差不多是log(n),也是非常優的解法


二、矩陣乘法解法

這個解法應該是這一道題的正解。

我是一個蒟蒻,還是只是初懂矩陣乘法的小白。

我就貼一下自己的代碼,詳細的題解還是看一下別的大佬的題解。

#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007 //Mod數
struct Matrix{//這個是矩陣的結構體
    long long ma[2][2];
};
Matrix mul(Matrix A,Matrix B)//矩陣乘法
{
    Matrix C;//答案矩陣
    C.ma[0][0]=C.ma[0][1]=C.ma[1][0]=C.ma[1][1]=0;//初始化
    for(int i=0;i<2;i++)
    {
        for(int j=0;j<2;j++)
        {
            for(int k=0;k<2;k++)
            {
                C.ma[i][j]=(C.ma[i][j]+A.ma[i][k]*B.ma[k][j])%mod;
            }
        }
    }
    return C;
}
Matrix pow_mod(Matrix A,long long n)//卡蘇米+矩陣乘法優化
{
    Matrix B;
    B.ma[0][0]=B.ma[1][1]=1;
    B.ma[0][1]=B.ma[1][0]=0;
    while(n) {
        if(n&1) B=mul(B,A);
        A=mul(A,A);
        n>>=1;
    }
    return B;
}
int main()
{
    long long n;
    Matrix A;
    A.ma[0][0]=1;A.ma[0][1]=1;
    A.ma[1][0]=1;A.ma[1][1]=0;//初始的數組
    Matrix ans=pow_mod(A,n);
    printf("%lld\n",ans.ma[0][1]);//輸出答案
    return 0;
}

註:關於矩陣乘法加速,這個矩陣有多種寫法,這個只是其中的一種,不需要糾結於矩陣是否唯一。

【洛谷P1962 斐波那契數列】矩陣快速冪+數學推導