1. 程式人生 > >矩陣加速數列(學習筆記)

矩陣加速數列(學習筆記)

span urn www define str problem 需要 struct printf

當我們遇到這樣一類問題:已知遞推式,但數據範圍太大,直接遞推下去肯定會超時,例如求斐波拉契數列的第n項(\(n<=10^{18}\))等等.這是我們就需要用到矩陣來加速遞推,優化時間復雜度.

步驟和思想都很簡單.以求斐波拉契數列的第n項為例.我們已知遞推式\(f(n)=f(n-2)+f(n-1)\),把等式兩邊各抽象為一個矩陣,即我們已知\(S(n-1)=[f(n-2),f(n-1)]\),要求出\(S(n)=[f(n-1),f(n)]\),那麽\(S(n-1)\)如何變換到\(S(n)\)呢?可以乘上狀態矩陣\(T= [\begin{matrix} 0 & 1 \\1 & 1 \end{matrix}]\)
,這裏可以自己根據矩陣乘法運算法則手玩出來.

於是就可以設出初始矩陣\(S(0)\),然後\(S(n)=S(0)*T^n\),顯然\(T^n\)可以通過矩陣快速冪來求得.my題解

看幾道模板題.

洛咕 my題解

洛咕

題意:\(a[1]=a[2]=a[3]=1\);\(a[x]=a[x-3]+a[x-1] (x>3)\).求a數列的第n項對1e9+7取余的值.

模板題,就直接放代碼了.

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
const int mod=1e9+7;
struct matrix{
    int a[3][3];
    matrix operator *(matrix b){
    matrix c;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            c.a[i][j]=0;
    for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            for(int k=0;k<3;k++)
                c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%mod;
    return c;
    }
}S,T;
int main(){
    int Q=read();
    while(Q--){
        S.a[0][0]=0;S.a[0][1]=1;S.a[0][2]=1;
        T.a[0][0]=0;T.a[0][1]=0;T.a[0][2]=1;
        T.a[1][0]=1;T.a[1][1]=0;T.a[1][2]=0;
        T.a[2][0]=0;T.a[2][1]=1;T.a[2][2]=1;
        int n=read();
        while(n){if(n&1)S=S*T;T=T*T;n>>=1;}
        printf("%d\n",S.a[0][0]);   
    }
    return 0;
}

洛咕

廣義的斐波那契數列是指形如\(a_n=p\times a_{n-1}+q\times a_{n-2}\)的數列.給定數列的兩系數\(p\)\(q\),以及數列的最前兩項\(a_1\)\(a_2\),另給出兩個整數\(n\)\(m\),試求數列的第\(n\)\(a_n\)除以\(m\)的余數.

分析:按照步驟列出已知矩陣\(S(n-1)=[f(n-1),f(n-2)]\),目標矩陣\(S(n)=[f(n),f(n-1)]\),手玩出轉移矩陣\(T=[\begin{matrix} p & 1 \\q & 0 \end{matrix}]\).

#include<bits/stdc++.h>
#define LL long long
using namespace std;
inline LL read(){
    LL s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
LL p,q,a1,a2,n,m;
struct matrix{
    LL a[2][2];
    matrix operator *(matrix b){
    matrix c;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            c.a[i][j]=0;
    for(int i=0;i<2;i++)
        for(int j=0;j<2;j++)
            for(int k=0;k<2;k++)
                c.a[i][j]=(c.a[i][j]+1LL*a[i][k]*b.a[k][j])%m;
    return c;
    }
}S,T;
int main(){
    p=read();q=read();a1=read();a2=read();n=read();m=read();
    S.a[0][0]=a2;S.a[0][1]=a1;
    T.a[0][0]=p;T.a[0][1]=1;
    T.a[1][0]=q;T.a[1][1]=0;
    n-=2;
    while(n){if(n&1)S=S*T;T=T*T;n>>=1;}
    printf("%lld\n",S.a[0][0]%m);   
    return 0;
}

矩陣加速數列(學習筆記)