矩陣運算
加減:要求行列數一致,對應位相加減
乘:對於 \(A * B\),答案 \(ans[i][j]=\sum a[i][k] * b[k][j]\),要求第一個矩陣列數等於第二個矩陣行數。注意矩陣乘法具有結合律但不具有交換律
矩陣求逆:需要用到行列式,暫咕
矩陣優化遞推
最常見的斐波那契有遞推式 \(f_i=f _ {i-1}+f _ {i-2}\)
如果只需要知道第 \(n\) 項的值(\(n\) 很大),可以這樣做:
定義初始矩陣:
\[A=\begin{bmatrix} f _ {i-2}& f _ {i-1} \end{bmatrix}\]
要乘一個轉移矩陣 \(B\) 結果為答案矩陣
\[C=\begin{bmatrix} f _ {i-1}& f _ {i} \end{bmatrix}\]
那么 \(B\) 可以構造為:
\[\begin{bmatrix} 0&1 \\ 1&1 \end{bmatrix}\]
通過定義可以證明這樣構造是對的
根據矩陣的結合律,最終要乘 \(n\) 次轉移矩陣,不妨先求轉移矩陣的 \(n\) 次方然后再乘初始矩陣
而 \(n\) 次方可以快速冪優化
下面采用重載運算符處理矩乘運算
代碼實現
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n;
struct Mat{
int a[2][2];
Mat(){a[0][0]=a[1][0]=a[0][1]=a[1][1]=0;}
};
Mat operator * (Mat b,Mat c){
Mat ans;
for(int i=0;i<=1;i++){
for(int j=0;j<=1;j++){
for(int k=0;k<=1;k++){
ans.a[i][j]=(ans.a[i][j]+b.a[i][k]*c.a[k][j]%mod)%mod;
}
}
}
return ans;
}
Mat po(Mat a,int b){
Mat ans;
ans.a[0][0]=ans.a[1][1]=1;
while(b){
if(b&1)ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
signed main(){
cin>>n;
if(n==1||n==2){
cout<<1;
return 0;
}
Mat c;
c.a[0][1]=c.a[1][0]=c.a[1][1]=1;
Mat p=po(c,n-2);
Mat b;
b.a[0][0]=b.a[0][1]=1;
b=b*p;
cout<<b.a[0][1];
return 0;
}
矩陣優化圖上問題
對於圖上的 \(dp\),有限制為與一個點有連邊的點才能轉移
而如果進行多次這樣的操作,可以用矩陣把狀態的轉移記錄下來,用快速冪優化
比如這道題,除了終點一個點可以向相鄰點連邊,那么表達在矩陣上即為 \((i,i+1)\) 以及 \((i,i-1)\) 的位置為 \(1\)
快速冪后即可知道方案數是多少了
代碼實現
#include<bits/stdc++.h>
using namespace std;
const int mod=1000;
const int m=8;
int n;
struct Mat{
int d[10][10];
Mat(){memset(d,0,sizeof d);}
};
Mat operator * (Mat a,Mat b){
Mat ans;
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
for(int k=1;k<=m;k++){
ans.d[i][j]+=a.d[i][k]*b.d[k][j]%mod;
}
}
}
for(int i=1;i<=m;i++)for(int j=1;j<=m;j++)ans.d[i][j]%=mod;
return ans;
}
Mat po(Mat a,int b){
Mat ans;
for(int i=1;i<=m;i++)ans.d[i][i]=1;
while(b){
if(b&1)ans=ans*a;
a=a*a;
b>>=1;
}
return ans;
}
int main(){
cin>>n;
Mat a;
for(int i=1;i<=m-1;i++){
if(i==4)continue;
a.d[i+1][i]=1;
}
for(int i=2;i<=m;i++){
if(i==6)continue;
a.d[i-1][i]=1;
}
a.d[m][1]=a.d[1][m]=1;
Mat ans=po(a,n);
Mat k;k.d[1][1]=1;
k=k*ans;
cout<<k.d[1][5];
return 0;
}