【学习笔记】矩阵快速幂
算法介绍
前置芝士:矩阵 矩阵乘法
这两个芝士并不是很难
重点可能在应用方面
首先介绍我所理解的广义的矩阵乘法:现在我们手上有两个矩阵\(A\)和\(B\),我们期望得到一个答案矩阵\(Ans\) 其中\(Ans[i][j]\ =\ Ans[i][j]\ +\ (A[i][k]\ *\ B[i][k]) 其中 +\ * 代一种运算(如 min\ max 等等)\)
都可以用矩阵快速幂把递推的时间从\(O(n)减到O(logn)\)
先放一下矩阵快速幂的代码吧 类比快速幂即可
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MOD 1000000007
using namespace std;
ll m,k;
inline ll read(){
ll f = 1,ans = 0;
char a = getchar();
while((a < '0' || a > '9') && a != '-') a = getchar();
if(a == '-') f = -1,a = getchar();
while(a <= '9' && a >= '0') ans = (ans << 3) + (ans << 1) + (a ^ 48),a = getchar();
return f * ans;
}
struct map{
ll a[200][200];
map(){
memset(a,0,sizeof(a));
}
void in(){
for(ll i = 1;i <= m;i++)
for(ll j = 1;j <= m;j++)
a[i][j] = read();
}
void out(){
for(ll i = 1;i <= m;i++,puts(""))
for(ll j = 1;j <= m;j++)
cout<<a[i][j]<<" ";
}
}ai,ans;
map operator * (const map &a,const map &b){
map c;
for(ll k = 1;k <= m;k++)
for(ll i = 1;i <= m;i++)
for(ll j = 1;j <= m;j++)
c.a[i][j] = (c.a[i][j] + (a.a[i][k] * b.a[k][j]) % MOD)% MOD;
return c;
}
void quick(ll k){
while(k){
if(k & 1) ans = ans * ai;
ai = ai * ai;
k >>= 1;
}
}
int main(){
m = read();
k = read() - 1;
ai.in();
ans = ai;
quick(k);
ans.out();
}
例题
前面说了 矩阵快速幂重点在于应用
下面给出两种我最近见到的题
构造矩阵
P1939 【模板】矩阵加速(数列)
根据题目,我们的目标矩阵是这样的
\[ \begin{matrix}
f[i] \\
f[i - 1] \\
f[i - 2]
\end{matrix}
\]
然后我们发现
\[ \begin{matrix}
f[i]=f[i−1]×1+f[i−2]×0+f[i−3]×1 \\
f[i−1]=f[i−1]×1+f[i−2]×0+f[i−3]×0 \\
f[i−2]=f[i−1]×0+f[i−2]×1+f[i−3]×00
\end{matrix}
\]
观察系数 我们构造出这样一个递归矩阵
\[ \begin{matrix}
1 & 0 & 1 \\
1 & 0 & 0 \\
0 & 1 & 0
\end{matrix}
\]
接下来就是套路的快速幂了
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define MOD 1000000007
using namespace std;
ll m = 3,k,T;
inline ll read(){
ll f = 1,ans = 0;
char a = getchar();
while((a < '0' || a > '9') && a != '-') a = getchar();
if(a == '-') f = -1,a = getchar();
while(a <= '9' && a >= '0') ans = (ans << 3) + (ans << 1) + (a ^ 48),a = getchar();
return f * ans;
}
struct map{
ll a[200][200];
map(){
memset(a,0,sizeof(a));
}
void aiin(){
memset(a,0,sizeof(a));
a[1][1] = a[1][3] = a[2][1] = a[3][2] = 1;
}
void ansin(){
memset(a,0,sizeof(a));
a[1][1] = a[2][2] = a[3][3] = 1;
}
}ai,ans;
map operator * (const map &a,const map &b){
map c;
for(ll k = 1;k <= 3;k++)
for(ll i = 1;i <= 3;i++)
for(ll j = 1;j <= 3;j++)
c.a[i][j] = (c.a[i][j] + (a.a[i][k] * b.a[k][j]) % MOD)% MOD;
return c;
}
void quick(ll k){
ans.ansin();
ai.aiin();
while(k){
if(k & 1) ans = ans * ai;
ai = ai * ai;
k >>= 1;
}
}
int main(){
T = read();
while(T --){
k = read();
if(k <= 3)
{puts("1");continue;}
else
quick(k),cout<<ans.a[2][1]<<endl;
}
}
题目自带的矩阵
P2886 [USACO07NOV]Cow Relays G
题目大意算法就不再阐述 请看这里