版权声明:本文为博主原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接和本声明。
矩阵快速幂(大佬们挑不懂的食用)
0 前置
0.1 什么是矩阵
"矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合”
—— 摘自百度百科
一看就不是人话
其实它就是个二维数组
0.2 矩阵の运算
A想直接看乘法的巨佬请自动跳到0.2.3(蒟蒻搞不来锚点)
最基础啦
C
=
A
+
B
⟷
C
i
,
j
=
A
i
,
j
+
B
i
,
j
C=A+B\longleftrightarrow C_{i,j}=A_{i,j}+B_{i,j}
C=A+B⟷Ci,j=Ai,j+Bi,j
举个栗子
[
1
2
3
4
5
6
7
8
9
]
+
[
10
11
12
13
14
15
16
17
18
]
=
[
11
13
15
17
19
21
23
25
27
]
\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix} + \begin{bmatrix} 10 & 11 & 12 \\ 13 & 14 & 15 \\ 16 & 17 & 18 \\ \end{bmatrix} \text{=} \begin{bmatrix} 11 & 13 & 15 \\ 17 & 19 & 21 \\ 23 & 25 & 27 \\ \end{bmatrix}
⎣⎡147258369⎦⎤+⎣⎡101316111417121518⎦⎤=⎣⎡111723131925152127⎦⎤
满足交换律和结合律,即
A
+
B
=
B
+
A
A+B=B+A
A+B=B+A,
A
+
B
+
C
=
A
+
(
B
+
C
)
A+B+C=A+(B+C)
A+B+C=A+(B+C)
也很基础
和整数运算一样哒,
A
−
B
=
A
+
(
−
B
)
A-B=A+(-B)
A−B=A+(−B)
即
\text{即}
即
C
=
A
−
B
⟷
C
i
,
j
=
A
i
,
j
−
B
i
,
j
C=A-B\longleftrightarrow C_{i,j}=A_{i,j}-B_{i,j}
C=A−B⟷Ci,j=Ai,j−Bi,j
重点来啦!
先敲黑板
不满足交换率!
不满足交换率!!
不满足交换率!!!
至于为什么,我们先看它的定义
两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。如A是m×n矩阵和B是n×p矩阵,它们的乘积C是一个m×p矩阵 ,它的一个元素: c i , j = a i , j b 1 , j + a i , 2 b 2 , j + ⋯ + a i , n b n , j = ∑ r = 1 n a i , r b r , j c_{i,j}=a_{i,j}b_{1,j}+a_{i,2}b_{2,j}+\cdots+a_{i,n}b_{n,j}=\sum_{r=1}^na_{i,r}b_{r,j} ci,j=ai,jb1,j+ai,2b2,j+⋯+ai,nbn,j=∑r=1nai,rbr,j
—— 摘自百度百科
什么鬼
通俗地来讲,矩阵乘法分这几步(假设
A
⋅
B
=
C
A\cdot B=C
A⋅B=C)
1 分A
我们把
A
A
A每行分开
e
.
g
.
e.g.
e.g.
[
1
2
3
4
5
6
7
8
9
]
→
[
1
2
3
]
,
[
4
5
6
]
,
[
7
8
9
]
\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix} \to [1\quad 2\quad 3],[4\quad 5\quad 6],[7\quad 8\quad 9]
⎣⎡147258369⎦⎤→[123],[456],[789]
2 旋转
把分出来的行转成竖着的列
e
.
g
.
e.g.
e.g.
[
1
2
3
]
→
[
1
2
3
]
[1\quad 2\quad 3]\to \begin{bmatrix} 1\\2\\3 \end{bmatrix}
[123]→⎣⎡123⎦⎤
3 分B
把 B B B每一列都分出来(同 A A A)
4 计算
经过上面3步,
A
,
B
A,B
A,B都变成了几个列,然后把这几列对应的位置乘起来再相加
e
.
g
.
e.g.
e.g.
[
1
2
3
]
⋅
[
6
5
4
]
→
1
⋅
6
+
2
⋅
5
+
3
⋅
4
=
28
\begin{bmatrix}1\\2\\3\end{bmatrix}\cdot\begin{bmatrix}6\\5\\4\end{bmatrix}\to 1\cdot6+2\cdot5+3\cdot4=28
⎣⎡123⎦⎤⋅⎣⎡654⎦⎤→1⋅6+2⋅5+3⋅4=28
对于
A
A
A的第
i
i
i列和
B
B
B的第
j
j
j列,假设经过如上计算得到了
S
S
S
那么……
C
i
,
j
C_{i,j}
Ci,j就等于
S
S
S啦!
e
.
g
e.g
e.g
A
=
[
1
2
3
3
]
,
B
=
[
5
4
4
2
3
3
]
,
那么
A=\begin{bmatrix}1&2\\3&3\end{bmatrix},B=\begin{bmatrix}5&4&4\\2&3&3\end{bmatrix},\text{那么}
A=[1323],B=[524343],那么
C
1
,
1
=
1
⋅
5
+
2
⋅
2
=
9
C_{1,1}=1\cdot5+2\cdot2=9
C1,1=1⋅5+2⋅2=9
C
1
,
2
=
1
⋅
4
+
2
⋅
3
=
10
C_{1,2}=1\cdot4+2\cdot3=10
C1,2=1⋅4+2⋅3=10
C
1
,
3
=
1
⋅
4
+
2
⋅
3
=
10
C_{1,3}=1\cdot4+2\cdot3=10
C1,3=1⋅4+2⋅3=10
C
2
,
1
=
3
⋅
5
+
3
⋅
2
=
21
C_{2,1}=3\cdot5+3\cdot2=21
C2,1=3⋅5+3⋅2=21
C
2
,
2
=
3
⋅
4
+
3
⋅
3
=
21
C_{2,2}=3\cdot4+3\cdot3=21
C2,2=3⋅4+3⋅3=21
C
2
,
3
=
3
⋅
4
+
3
⋅
3
=
21
C_{2,3}=3\cdot4+3\cdot3=21
C2,3=3⋅4+3⋅3=21
∴
C
=
[
9
10
10
21
21
21
]
\therefore C=\begin{bmatrix}9&10&10\\21&21&21\end{bmatrix}
∴C=[92110211021]
其实这个过程和定义里写的是等价哒
毕竟百度百科说的都不是人话
某巨佬
w
b
k
wbk
wbk:诶等等,如果进行第4步的时候两列的长度不一样怎么办?比如
[
1
2
]
\begin{bmatrix}1\\2\end{bmatrix}
[12]和
[
1
2
3
]
\begin{bmatrix}1\\2\\3\end{bmatrix}
⎣⎡123⎦⎤
别急,仔细看矩阵乘法的定义
两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义
所以矩阵乘法是有前提哒,这样计算才不会出问题(从这儿就能看出为什么不满足交换率,毕竟换个顺序可能就算不了了)
同时注意到若 A A A有 a a a行, B B B有 b b b列,聪明的您肯定很快就会发现 C C C为 a a a行 b b b列的矩阵,便可以确定 C C C矩阵的大小。也就是说若 C = A ⋅ B C=A\cdot B C=A⋅B,那么 C C C的行的数量不会超过 A A A,且列的数量不会超过 B B B,所以开数组不需要开额外的大小。
5 code(敲黑板!!!)
能背就背
模板这种东西最好一开始就找一种自己看着舒服的,然后一直写这一种,尽量不要中途更换写法,否则…
—— 某曾换过码风的巨佬的惨案
下面附上本蒟蒻重载运算符的代码(重载运算符后快速幂直接套,比较方便),仅供参考
int n; //矩阵的大小
struct Matrix{
ll a[MAXN][MAXN]; //大小视题而定
Matrix(){memset(a,0,sizeof a);} //构造函数(不知道什么是构造函数的童鞋们建议学一下,您们可以把初始化的内容放到里面,很方便)
void Out(){ //查错用的(输出矩阵)
for(register int i=1; i<=n; i++){
for(register int j=1; j<=m; j++)
printf("%-5lld\n",a[i][j]);
printf("\n");
}
}
Matrix operator *(Matrix k){ //重载运算符
Matrix res; //这里不写上面那个构造函数的话记得手动初始化一下
//memset(res.a,0,sizeof(res.a)); //手动初始化
for(register int i=1; i<=n; i++) //枚举A的每一行
for(register int j=1; j<=n; j++) //枚举B的每一列
for(register int l=1; l<=n; l++) //计算
res.a[i][j] = (res.a[i][j]+a[i][l]*k.a[l][j]%MOD)%MOD;
return res;
}
}E;
int main(){
for(register int i=1; i<=n; i++) E[i][i]=1; //构造原始矩阵,相当于整数中的1
}
1 矩阵快速幂
1.1 本质
快速幂其实是用来优化乘法的,我们直接看矩阵乘法是拿来优化什么的
矩阵乘法是一种用来优化递推式的东西
—— 某巨佬xinyue
我们观察一下矩阵乘法的计算方式,在 A ⋅ B = C A\cdot B=C A⋅B=C中,对于 A i , j A_{i,j} Ai,j,它会分别乘以 B B B的第 j j j行的每一项 B j , k B_{j,k} Bj,k,然后分别累加到 C i , k C_{i,k} Ci,k(代码意译)
重点来了
如果
A
A
A只有一行……
那么根据矩阵乘法的性质……
诶,
C
C
C好像也只有一行,而且和
A
A
A有一样的列数
于是我们便可以将
A
A
A和
C
C
C从两个矩阵(二维数组)变成两个长度相同的序列(一维数组)
再看运算过程,用之前的方法,聪明的您会很快地发现经过操作后的 A A A只有一列,也就是说对于 C C C中的每一项 C i C_i Ci, C i = A 1 ⋅ B 1 , i + A 2 ⋅ B 2 , i + … A n ⋅ B n , i C_i=A_1\cdot B_{1,i}+A_2\cdot B_{2,i}+…A_n\cdot B_{n,i} Ci=A1⋅B1,i+A2⋅B2,i+…An⋅Bn,i
注意到
A
A
A的每一项和
B
B
B的行数在运算过程中一直是没变的,在变的只有
B
B
B的列数
于是我们还是采用之前的运算方法,把
B
B
B的每一列提出来,然后…
我们把提出来的每一个序列
i
:
(
b
1
,
b
2
,
…
,
b
n
)
i:(b_1,b_2,…,b_n)
i:(b1,b2,…,bn)抽象成一个操作,把对应的
c
(
即
C
i
)
c(\text{即}C_i)
c(即Ci)抽象成结果,那么聪明的您会很快地发现:
矩阵的运算本质就是对一个序列
A
A
A进行一堆操作并分别保存所有结果
我们要做的就是构造出初始序列
A
A
A(一般会告诉你)和操作矩阵
B
B
B,套用快速幂优化矩阵乘法,求出答案
1.2 举个栗子
著名的斐波那契数列
已知 f 1 = 1 , f 2 = 1 , f n = f n − 1 + f n − 2 f_1=1,f_2=1,f_n=f_{n-1}+f_{n-2} f1=1,f2=1,fn=fn−1+fn−2,求 f n f_n fn
首先写出递推式, f n = f n − 1 + f n − 2 f_n=f_{n-1}+f_{n-2} fn=fn−1+fn−2
然后构造出初始序列
A
A
A,注意到推出
f
n
f_n
fn需要知道
f
n
−
1
,
f
n
−
2
f_{n-1},f_{n-2}
fn−1,fn−2
∴
A
\therefore A
∴A要包含
f
n
−
1
f_{n-1}
fn−1和
f
n
−
2
f_{n-2}
fn−2
∴
A
=
[
f
n
−
1
f
n
−
2
…
]
(
省略号指后面可能还要保存其它东西
)
\therefore A=[f_{n-1}\quad f_{n-2}\quad …](\text{省略号指后面可能还要保存其它东西})
∴A=[fn−1fn−2…](省略号指后面可能还要保存其它东西)
注意到
A
′
=
[
f
n
f
n
−
1
…
]
A'=[f_n\quad f_{n-1}\quad …]
A′=[fnfn−1…],其中
f
n
=
f
n
−
1
+
f
n
−
2
f
n
−
1
=
f
n
−
1
f_n=f_{n-1}+f_{n-2}\;\;\;\;\;\;\;\;\;\;\;f_{n-1}=f_{n-1}
fn=fn−1+fn−2fn−1=fn−1
∴
\therefore
∴ 只保存两项
f
n
−
1
,
f
n
−
2
f_{n-1},f_{n-2}
fn−1,fn−2便可以求出下一个
A
′
A'
A′
那么
A
A
A就构造好了:
A
=
[
f
n
−
1
f
n
−
2
]
A=[f_{n-1}\;\;\;\;f_{n-2}]
A=[fn−1fn−2]
然后构造操作矩阵
B
B
B
先写出由
A
A
A推出
A
′
A'
A′的过程:
A
′
=
[
f
n
−
1
+
f
n
−
2
f
n
−
1
]
=
[
A
1
+
A
2
A
1
]
A'=[f_{n-1}+f_{n-2}\quad f_{n-1}]=[A_1+A_2\quad A_1]
A′=[fn−1+fn−2fn−1]=[A1+A2A1]
考虑结果序列
C
C
C的每一项要对应
A
′
A'
A′,稍微变一下形
C
=
A
′
=
[
A
1
⋅
1
+
A
2
⋅
1
A
1
⋅
1
+
A
2
⋅
0
]
C=A'=[A_1\cdot1+A_2\cdot1\quad A_1\cdot1+A_2\cdot0]
C=A′=[A1⋅1+A2⋅1A1⋅1+A2⋅0]
聪明的您观察系数便可以很快地发现操作序列分别为:
[
1
1
]
和
[
1
0
]
[1\quad 1]\text{和}[1\quad 0]
[11]和[10]
所以便很快地构造出操作矩阵
B
:
B:
B:
B
=
[
1
1
1
0
]
B=\begin{bmatrix}1&1\\1&0\end{bmatrix}
B=[1110]
先把结果写出来:
f
n
=
f
n
−
1
+
f
n
−
2
f_n=f_{n-1}+f_{n-2}
fn=fn−1+fn−2
同时
[
f
n
−
1
f
n
−
2
]
=
[
f
n
−
2
f
n
−
3
]
⋅
B
=
⋯
=
[
f
2
f
1
]
⋅
B
n
−
3
=
A
⋅
B
n
−
3
[f_{n-1}\quad f_{n-2}]=[f_{n-2}\quad f_{n-3}]\cdot B=\cdots=[f_2\quad f_1]\cdot B^{n-3}=A\cdot B^{n-3}
[fn−1fn−2]=[fn−2fn−3]⋅B=⋯=[f2f1]⋅Bn−3=A⋅Bn−3
然后就可以很愉快地用矩阵乘法快速幂求出
A
⋅
B
n
−
3
A\cdot B^{n-3}
A⋅Bn−3啦!
代码见下面例题
2 例题(不定时更新)
2.1 Luogu P1962 斐波那契数列
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch;bool flag=0;
while(!isdigit(ch=getchar()))if(ch=='-')flag=1;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
typedef long long ll;
const ll MOD = 1000000007;
struct Matrix{
ll a[3][3];
Matrix(){memset(a,0,sizeof a);}
void Out(){
for(register int i=1; i<=2; i++){
for(register int j=1; j<=2; j++)
printf("%-5lld\n",a[i][j]);
printf("\n");
}
}
Matrix operator *(Matrix k){
Matrix res;
//memset(res.a,0,sizeof(res.a));
for(register int i=1; i<=2; i++)
for(register int j=1; j<=2; j++)
for(register int l=1; l<=2; l++)
res.a[i][j] = (res.a[i][j]+a[i][l]*k.a[l][j]%MOD)%MOD;
return res;
}
}E,base,f;
ll n;
inline Matrix QuickPow(Matrix Base, ll k){
Matrix res=E;
while(k){
if(k&1)
res = res*Base;
Base = Base*Base;
k>>=1;
}
return res;
}
int main(){
E.a[1][1]=E.a[1][2]=1;
base.a[1][1]=base.a[1][2]=base.a[2][1]=1;
f.a[1][1]=f.a[1][2]=1;
Read(n);
if(n<=2){
cout<<1<<endl;
return 0;
}
cout<<(f*QuickPow(base,n-2)).a[1][1]<<endl;
return 0;
}
2.2 Luogu P1939 【模板】矩阵加速(数列)
题目链接
思路类似,但细节上稍微有些不一样
一次构造
考虑构造初始序列
A
A
A
题目求
f
n
f_n
fn,注意到
f
n
=
f
n
−
3
+
f
n
−
1
f_n=f_{n-3}+f_{n-1}
fn=fn−3+fn−1
所以
A
=
[
f
n
−
1
f
n
−
3
⋯
]
A=[f_{n-1}\quad f_{n-3}\quad \cdots]
A=[fn−1fn−3⋯]
A
′
=
[
f
n
f
n
−
2
⋯
]
A'=[f_{n}\quad f_{n-2}\quad \cdots]
A′=[fnfn−2⋯]
二次构造
然后问题来了,
f
n
f_n
fn的确可以推出来,但是
f
n
−
2
f_{n-2}
fn−2却不能用
f
n
−
1
f_{n-1}
fn−1和
f
n
−
3
f_{n-3}
fn−3推出来
所以我们还要再保存几个能推出
f
n
−
2
f_{n-2}
fn−2的值,这里直接保存
f
n
−
2
f_{n-2}
fn−2就好了,于是
A
=
[
f
n
−
1
f
n
−
2
f
n
−
3
⋯
]
A=[f_{n-1}\quad f_{n-2}\quad f_{n-3}\quad \cdots]
A=[fn−1fn−2fn−3⋯]
A
′
=
[
f
n
f
n
−
1
f
n
−
2
⋯
]
A'=[f_{n}\quad f_{n-1}\quad f_{n-2}\quad \cdots]
A′=[fnfn−1fn−2⋯]
检查一下,对于
A
′
A'
A′的每一项:
f
n
=
f
n
−
1
+
f
n
−
3
f_n=f_{n-1}+f_{n-3}
fn=fn−1+fn−3
f
n
−
1
=
f
n
−
1
f_{n-1}=f_{n-1}
fn−1=fn−1
f
n
−
2
=
f
n
−
2
f_{n-2}=f_{n-2}
fn−2=fn−2
所以
A
A
A便构造出来啦
同时初始序列
A
=
[
1
1
1
]
A=[1\quad1\quad1]
A=[111]
构造操作
再构造操作矩阵
B
B
B
观察前文从
A
A
A递推到
A
′
A'
A′的方式,便快速得出操作序列分别为:
[
1
0
1
]
[1\quad 0\quad 1]
[101]
[
1
0
0
]
[1\quad 0\quad 0]
[100]
[
0
1
0
]
[0\quad 1\quad 0]
[010]
再拼在一起(注意这里我们构造的是列,拼的时候要转一下)便得到了操作矩阵
B
=
[
1
1
0
0
0
1
1
0
0
]
B=\begin{bmatrix}1&1&0\\0&0&1\\1&0&0\end{bmatrix}
B=⎣⎡101100010⎦⎤
A
n
s
=
A
⋅
B
n
Ans=A\cdot B^{n}
Ans=A⋅Bn
代码就不给了,自己写一写要不然博客太长了
2.3 Vijos 1067 Warcraft III 守望者的烦恼
一次构造
构造初始序列
A
A
A
考虑所求值
f
n
f_n
fn,第
n
n
n个位置可以由第
n
−
1
,
n
−
2
,
n
−
3
,
⋯
,
n
−
k
n-1,n-2,n-3,\cdots,n-k
n−1,n−2,n−3,⋯,n−k个位置闪过来,所以递推方程:
f
n
=
∑
i
=
n
−
k
n
−
1
f
i
f_n=\sum_{i=n-k}^{n-1}f_i
fn=i=n−k∑n−1fi
所以直接把每一个
f
i
f_i
fi都存进来
A
=
[
f
n
−
1
f
n
−
2
⋯
f
n
−
k
⋯
]
A=[f_{n-1}\quad f_{n-2}\quad \cdots \quad f_{n-k} \quad \cdots]
A=[fn−1fn−2⋯fn−k⋯]
检验一下
A
′
=
[
f
n
f
n
−
1
⋯
f
n
−
k
+
1
⋯
]
A'=[f_n\quad f_{n-1}\quad \cdots \quad f_{n-k+1}\quad \cdots]
A′=[fnfn−1⋯fn−k+1⋯]
其中
f
n
=
f
n
−
1
+
f
n
−
2
+
⋯
+
f
n
−
k
f_n=f_{n-1}+f_{n-2}+\cdots+f_{n-k}
fn=fn−1+fn−2+⋯+fn−k
f
n
−
1
=
f
n
−
1
f_{n-1}=f_{n-1}
fn−1=fn−1
⋯
\cdots
⋯
f
n
−
k
+
1
=
f
n
−
k
+
1
f_{n-k+1}=f_{n-k+1}
fn−k+1=fn−k+1
所以可以从
A
A
A递推出
A
′
A'
A′
构造操作
对于
f
n
f_n
fn,操作序列为
[
1
1
⋯
1
]
(
k
个
)
[1\quad1\quad\cdots\quad1](k\text{个})
[11⋯1](k个)
对于其他的项,操作序列分别为:
[
1
0
0
0
⋯
0
]
[1\quad0\quad0\quad0\quad\cdots\quad0]
[1000⋯0]
[
0
1
0
0
⋯
0
]
[0\quad1\quad0\quad0\quad\cdots\quad0]
[0100⋯0]
[
0
0
1
0
⋯
0
]
[0\quad0\quad1\quad0\quad\cdots\quad0]
[0010⋯0]
⋯
\cdots
⋯
[
0
0
0
⋯
1
0
]
[0\quad0\quad0\quad\cdots\quad1\quad0]
[000⋯10]
∴
B
=
[
1
1
0
⋯
0
1
0
1
⋯
0
⋯
1
0
0
⋯
1
1
0
0
⋯
0
]
\therefore B=\begin{bmatrix}1&1&0&\cdots&0\\1&0&1&\cdots&0\\&&\cdots\\1&0&0&\cdots&1\\1&0&0&\cdots&0\end{bmatrix}
∴B=⎣⎢⎢⎢⎢⎡1111100001⋯00⋯⋯⋯⋯0010⎦⎥⎥⎥⎥⎤
2.4 等比数列求和
未知来源
众所周知等比数列有求和公式可以直接用,然而某毒瘤出题人xinyue就是要让你对合数取模,所以我们就考虑不用除法的方法——矩阵快速幂!(当然,大佬们可以随意用数论处理合数模数的方法暴踩我)
一次构造
显然
S
n
=
S
n
−
1
+
p
n
S_n=S_{n-1}+p^n
Sn=Sn−1+pn
所以构造
A
=
[
S
n
−
1
p
n
−
1
]
A=[S_{n-1}\quad p^{n-1}]
A=[Sn−1pn−1]
考虑递推
A
′
=
[
S
n
p
n
]
A'=[S_n\quad p^n]
A′=[Snpn]
递推过程显然
S
n
=
S
n
−
1
+
p
n
−
1
S_n=S_{n-1}+p^{n-1}
Sn=Sn−1+pn−1
p
n
=
p
n
−
1
⋅
p
p^n=p^{n-1}\cdot p
pn=pn−1⋅p
A
A
A便构造好啦
构造操作
再构造操作矩阵,同样观察系数构造出操作序列:
[
1
1
]
[1\quad 1]
[11]
[
0
p
]
[0\quad p]
[0p]
于是操作矩阵
B
=
[
1
0
1
p
]
B=\begin{bmatrix}1&0\\1&p\end{bmatrix}
B=[110p]
3 小结
3.1 难点
很多人都以为矩阵快速幂难点在于递推式,其实我觉得它真正的重难点在于构造初始序列
A
A
A,但这其实也是一个很简单的过程。这个过程和
B
F
S
BFS
BFS十分地相似,先将所求的
f
n
f_n
fn存入“队列”,每次取出“队头”,将推出“队头”需要的东西再次存入“队列”,直到“队列”为空为止。
3.2 常见问题
矩阵快速幂的操作矩阵
B
B
B可以有很多种不同的样子,毕竟初始序列
A
A
A可以有很多不同的写法,所以当你发现你和小伙伴的写法不一样的时候,不要去盲目修改自己的写法。毕竟一万个矩阵快速幂的题解有一万种不同的操作矩阵
3.3 建议
把矩阵用结构体存起来,同时重载乘号,这样写快速幂的时候可以直接套用快速幂的模板,而且代码看起来也很简洁
4 致谢
感谢wbk大佬用一晚上的时间和我一起研究矩阵
感谢xinyue的题库和讲解