矩陣加速數列(學習筆記)
阿新 • • 發佈:2019-03-24
span urn www define str problem 需要 struct printf 步驟和思想都很簡單.以求斐波拉契數列的第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}]\)
當我們遇到這樣一類問題:已知遞推式,但數據範圍太大,直接遞推下去肯定會超時,例如求斐波拉契數列的第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; }
矩陣加速數列(學習筆記)