矩阵

本质是一个二维数表

A=[a1,1a1,2a1,na2,1a2,2a1,nam,1am,2am,n]\mathbf A=\begin{bmatrix} a_{1,1} & a_{1,2} & \dots & a_{1,n} \\ a_{2,1} & a_{2,2} & \dots & a_{1,n} \\ \vdots &\vdots & \ddots & \vdots \\ a_{m,1} & a_{m,2} & \dots & a_{m,n} \\ \end{bmatrix}

矩阵乘法

A\mathbf Am×nm \times n 的矩阵,B\mathbf Bn×pn \times p 的矩阵,C=A×B\mathbf C = \mathbf A \times \mathbf BC\mathbf C 中第 ii 行第 jj 列元素可以表示为:

ci,j=k=1nai,k×bk,jc_{i,j}=\sum_{k=1}^{n}a_{i,k}\times b_{k,j}

示例:

[125346]×[789101112]=[(1×7+2×9+5×11)(1×8+2×10+5×12)(3×7+4×9+6×11)(3×8+4×10+6×12)]=[8088123136]\begin{aligned} \begin{bmatrix} 1 & 2 & 5\\ 3 & 4 & 6\\ \end{bmatrix} \times \begin{bmatrix} 7 & 8 \\ 9 & 10 \\ 11 & 12 \end{bmatrix} &= \begin{bmatrix} (1\times7+2\times9+5\times11)&(1\times8+2\times10+5\times12)\\ (3\times7+4\times9+6\times11)&(3\times8+4\times10+6\times12)\\ \end{bmatrix} \\ &= \begin{bmatrix} 80 & 88\\ 123 &136\\ \end{bmatrix} \end{aligned}

换句话说,就是用矩阵 A\mathbf A 的第 ii 行元素乘矩阵 B\mathbf B 的第 jj 列元素求和。

广义矩阵乘法

ci,j=mink=1n(ai,k+bk,j)ci,j=k=1nmax(ai,k,bk,j)c_{i,j}=\min _{k=1}^{n}(a_{i,k}+b_{k,j}) \\ c_{i,j}=\sum _{k=1}^{n}\max(a_{i,k},b_{k,j}) \\ \vdots

广义矩阵乘法可以是自行定义的运算,可以用来解决各种递推问题。但要保证满足结合律,以使用矩阵快速幂。

矩阵乘法的性质

事实上,矩阵乘法满足幺半群的性质。

  1. 矩阵乘法有封闭性:
    显然,矩阵与矩阵相乘,得到的还是矩阵。

  2. 矩阵乘法有结合律:

    D=A×B×C=A×(B×C)\mathbf D = \mathbf A \times \mathbf B \times \mathbf C = \mathbf A \times \left( \mathbf {B} \times \mathbf {C} \right)

    尝试从代数角度进行证明

    di,j=l=1Bcol(k=1Brow(ai,k×bk,l)×cl,j)=l=1Bcolk=1Browai,k×bk,l×cl,j=k=1Browai,k×(l=1Bcolbk,l×cl,j)\begin{aligned} d_{i,j}&=\sum_{l=1}^{\mathbf B_{col}}\left(\sum_{k=1}^{\mathbf B_{row}}\left(a_{i,k}\times b_{k,l}\right)\times c_{l,j} \right)\\ &=\sum_{l=1}^{\mathbf B_{col}}\sum_{k=1}^{\mathbf B_{row}}a_{i,k}\times b_{k,l}\times c_{l,j}\\ &=\sum_{k=1}^{\mathbf B_{row}}a_{i,k}\times \left(\sum_{l=1}^{\mathbf B_{col}}b_{k,l}\times c_{l,j} \right) \end{aligned}

  3. 矩阵乘法存在幺元
    定义单位矩阵

    In=[100010001]\mathbf I_n = \begin{bmatrix}1 & 0 &\dots & 0\\ 0 & 1 &\dots & 0\\\vdots & \vdots &\ddots & \vdots\\ 0 & 0 &\dots & 1\\\end{bmatrix}

则有A×In=A\mathbf A\times \mathbf I_n = \mathbf A

矩阵快速幂

洛谷P3390 【模板】矩阵快速幂

由于矩阵乘法满足结合律,可以利用倍增的思想,在对数复杂度内解决矩阵幂运算的问题。代码复杂度 O(n3logk)O(n^3\log k)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<bits/stdc++.h>
using namespace std;
#define ll long long

const int N = 105;
const ll MOD = 1e9+7;

struct Matrix{
int row,col;
ll d[N][N];
Matrix(int r,int c){
row=r,col=c;
memset(d,0,sizeof(d));
}
};

Matrix time(Matrix a,Matrix b){
Matrix c(a.row,b.col);
if(a.col != b.row)return c;
for(int i=0;i<a.row;i++){
for(int j=0;j<b.col;j++){
for(int k=0;k<a.col;k++){
c.d[i][j] += a.d[i][k] * b.d[k][j];
c.d[i][j] %= MOD;
}
}
}
return c;
}

Matrix pow(Matrix a,ll p){
Matrix res(a.col,a.col);
for(int i=0;i<a.col;i++){
res.d[i][i]=1;
}

while(p>0){
if(p&1)res=time(res,a);
p>>=1;
a=time(a,a);
}
return res;
}



int main(){
ll n,k;
scanf("%lld%lld",&n,&k);
Matrix a(n,n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&a.d[i][j]);
}
}

auto ans = pow(a,k);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%lld ",ans.d[i][j]);
}
puts("");
}
return 0;
}

矩阵快速幂加速递推

洛谷P1962 斐波那契数列

题意:求斐波那契数列第nn项对109+710^9+7求余的结果,n<263n<2^{63}
显然如果递推的话,复杂度 O(n)O(n),肯定会超时。
这时,我们考虑用矩阵乘法加速。
由递推式Fn=Fn1+Fn2F_n=F_{n-1}+F_{n-2},我们可以列出如下等式:

[FnFn1]=[Fn1Fn2]×[1110]\begin{bmatrix}F_n & F_{n-1}\end{bmatrix} = \begin{bmatrix}F_{n-1} & F_{n-2}\end{bmatrix} \times \begin{bmatrix}1 & 1\\1 & 0\end{bmatrix}

[FnFn1]=[F2F1]×[1110]n2\begin{bmatrix}F_n & F_{n-1}\end{bmatrix} = \begin{bmatrix}F_2 & F_1\end{bmatrix} \times \begin{bmatrix}1 & 1\\1 & 0\end{bmatrix}^{n-2}

时间复杂度 O(logn)O(\log n)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const ll MOD = 1e9+7;

struct Matrix{
int row,col;
ll d[5][5];
Matrix(int r,int c){
row=r,col=c;
memset(d,0,sizeof(d));
}
};
Matrix time(Matrix a,Matrix b){
Matrix c(a.row,b.col);
if(a.col != b.row)return c;
for(int i=0;i<a.row;i++){
for(int j=0;j<b.col;j++){
for(int k=0;k<a.col;k++){
c.d[i][j] += a.d[i][k] * b.d[k][j];
c.d[i][j] %= MOD;
}
}
}
return c;
}
Matrix pow(Matrix a,ll p){
Matrix res(a.col,a.col);
for(int i=0;i<a.col;i++){
res.d[i][i]=1;
}

while(p>0){
if(p&1)res=time(res,a);
p>>=1;
a=time(a,a);
}
return res;
}

int main(){
ll n;
scanf("%lld",&n);
if(n<=2){
puts("1");
return 0;
}
Matrix a(2,2);
a.d[0][0]=a.d[0][1]=a.d[1][0]=1;
Matrix b(1,2);
b.d[0][0] = b.d[0][1] = 1;
auto ans = time(b,pow(a,n-2));
printf("%lld\n",ans.d[0][0]);
return 0;
}

求斐波那契数列前 nn 项和

此时,有两个递推式Fn=Fn1+Fn2F_n=F_{n-1}+F_{n-2}Sn=Sn1+FnS_n=S_{n-1}+F_{n}。我们可以将两个递推式写在同一个等式中。

[FnFn1Sn]=[Fn1Fn2Sn1]×[111101001]\begin{bmatrix}F_n & F_{n-1} & S_n\end{bmatrix} = \begin{bmatrix}F_{n-1} & F_{n-2} & S_{n-1}\end{bmatrix} \times \begin{bmatrix} 1 & 1 & 1 \\ 1 & 0 & 1 \\ 0 & 0 & 1 \\ \end{bmatrix}

[HDU2157]How many ways??

题意:给定一个有向图,问从A点恰好走k步(允许重复经过边)到达B点的方案数mod p的值。
有递推式

Fu,k=(v,u)EFv,k1F_{u,k} = \sum_{(v,u)\in E}F_{v,k-1}

可以构建如下矩阵

[F1,kF2,kFn,k]=[F1,k1F2,k1Fn,k1]×[a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n]\begin{bmatrix}F_{1,k} & F_{2,k} & \dots & F_{n,k}\end{bmatrix}=\begin{bmatrix}F_{1,k-1} & F_{2,k-1} & \dots & F_{n,k-1}\end{bmatrix} \times \begin{bmatrix} a_{1,1} & a_{2,1} & \dots & a_{n,1} \\ a_{1,2} & a_{2,2} & \dots & a_{n,2} \\ \vdots & \vdots & \ddots & \vdots \\ a_{1,n} & a_{2,n} & \dots & a_{n,n} \\ \end{bmatrix}

其中 ai,ja_{i,j} 为从 iijj 的边的个数,在没有重边的情况下,这个矩阵正好等于这张图的邻接矩阵。

[POJ3613]恰好k步最短路

题意:给定一个有向带权图,问从A点恰好走k步(允许重复经过边)到达B点的最短路。
有递推式

Fu,k=min(v,u)E(Fv,k1+wu,v)F_{u,k} = \min_{(v,u)\in E}(F_{v,k-1}+w_{u,v})

考虑定义矩阵乘法

ci,j=mink=1n(ai,k+bk,j)c_{i,j}=\min _{k=1}^{n}(a_{i,k}+b_{k,j}) \\

则有

[F1,kF2,kFn,k]=[F1,k1F2,k1Fn,k1]×[w1,1w2,1wn,1w1,2w2,2wn,2w1,nw2,nwn,n]\begin{bmatrix}F_{1,k} & F_{2,k} & \dots & F_{n,k}\end{bmatrix}=\begin{bmatrix}F_{1,k-1} & F_{2,k-1} & \dots & F_{n,k-1}\end{bmatrix} \times \begin{bmatrix} w_{1,1} & w_{2,1} & \dots & w_{n,1} \\ w_{1,2} & w_{2,2} & \dots & w_{n,2} \\ \vdots & \vdots & \ddots & \vdots \\ w_{1,n} & w_{2,n} & \dots & w_{n,n} \\ \end{bmatrix}

问题解决。