矩阵
一个 3×33\times 33×3 的矩阵 AAA:
A=∣110101010∣ A=\begin{vmatrix}1&1&0\\1&0&1\\0&1&0\end{vmatrix} A= 110101010
用 Ai,jA_{i,j}Ai,j 表示矩阵 AAA 的第 iii 行第 jjj 列,和二维数组差不多。
矩阵乘法
矩阵乘法
若一个 N×MN\times MN×M 的矩阵与一个 X×YX\times YX×Y 的矩阵相乘,前提条件是:M=XM=XM=X。即两个矩阵相乘,第一个矩阵的列数要与第二个矩阵的行数相等。
即需要一个 N×MN\times MN×M 的矩阵与 M×KM\times KM×K 的矩阵相乘。
若一个 N×MN\times MN×M 的矩阵 AAA 乘上 M×KM\times KM×K 的矩阵 BBB,记 C=A×BC=A\times BC=A×B。
则有:
Ci,j=∑k=1MAi,k×Bk,j C_{i,j}=\sum_{k=1}^{M}A_{i,k}\times B_{k,j} Ci,j=k=1∑MAi,k×Bk,j
如:
∣235∣×∣342∣=∣2×3+3×4+5×2∣=∣28∣ \begin{vmatrix}2&3&5\end{vmatrix}\times\begin{vmatrix}3\\4\\2\end{vmatrix}=\begin{vmatrix}2\times 3+3\times 4+5\times 2\end{vmatrix}=\begin{vmatrix}28\end{vmatrix} 235 × 342 = 2×3+3×4+5×2 = 28
一些特性
矩阵乘法中的 1
如果我们用一个 2×22\times 22×2 的矩阵 AAA 去乘上 ∣1001∣\begin{vmatrix}1&0\\0&1\end{vmatrix} 1001 ,你会发现得到的矩阵 BBB 与矩阵 AAA 是一样的。
一个 N×NN\times NN×N 的矩阵 CCC,满足 Ci,iC_{i,i}Ci,i 都是 1,其余都是 1,它在矩阵乘法中与数字乘法中 1 的作用相同。
没有交换律
A×B≠B×AA\times B\not= B\times AA×B=B×A,这在矩阵乘法中是成立的。
如
∣1221∣×∣2232∣=∣8676∣ \begin{vmatrix}1&2\\2&1\end{vmatrix}\times\begin{vmatrix}2&2\\3&2\end{vmatrix}=\begin{vmatrix}8&6\\7&6\end{vmatrix} 1221 × 2322 = 8766
但是
∣2232∣×∣1221∣=∣6678∣ \begin{vmatrix}2&2\\3&2\end{vmatrix}\times\begin{vmatrix}1&2\\2&1\end{vmatrix}=\begin{vmatrix}6&6\\7&8\end{vmatrix} 2322 × 1221 = 6768
所以,在矩阵乘法中,不存在交换律。
其实不用具体运算也能得到。
一个 N×MN\times MN×M 的矩阵与 M×KM\times KM×K 的矩阵相乘(N≠KN\not=KN=K),这显然是合法的,但是若交换,M×KM\times KM×K 的矩阵明显不能乘上 N×MN\times MN×M 的矩阵。
结合律
虽然 A×B=B×AA\times B=B\times AA×B=B×A 是不成立的,但是存在 A×B×C=A×(B×C)A\times B\times C=A\times(B\times C)A×B×C=A×(B×C)。
从合法性的角度来说,有:
(N×M)⋅(M×K)⋅(K×L)=(N×M)⋅[(M×K)⋅(K×L)]=(N×L) (N\times M)\cdot(M\times K)\cdot(K\times L)=(N\times M)\cdot[(M\times K)\cdot(K\times L)]=(N\times L) (N×M)⋅(M×K)⋅(K×L)=(N×M)⋅[(M×K)⋅(K×L)]=(N×L)
可以自己用几个矩阵来验证一下。
矩阵快速幂
快速幂
如果我们遇到需要求 abmod pa^b\mod pabmodp,但是 bbb 特别大,如 101810^{18}1018,这时候肯定不能循环 bbb 次求了。
首先我们知道,使用 20,21,22,23,24⋯2^0,2^1,2^2,2^3,2^4\cdots20,21,22,23,24⋯ 可以通过加和组成任何正整数。
还知道 ab×ac=ab+ca^b\times a^c=a^{b+c}ab×ac=ab+c。
所以我们可以通过 a20,a21,a22,a23,a24⋯a^{2^0},a^{2^1},a^{2^2},a^{2^3},a^{2^4}\cdotsa20,a21,a22,a23,a24⋯ 相乘得到任何 aba^bab。
所以我们可以将 bbb 拆分成若干个 2 的次方相加。
同样的,这种快速求次方的方法也可以运用到其他运算,前提条件是其满足结合律。
矩阵快速幂
矩阵乘法也是一个满足结合律的运算。
有一个矩阵 A(N×N)A(N\times N)A(N×N),计算 A2kA^{2k}A2k,可以拆分成 Ak×AkA^k\times A^kAk×Ak。
数字中有 x0(x≠0)=1x^0(x\not=0)=1x0(x=0)=1,矩阵中也有矩阵 A0A^0A0 为矩阵乘法中的 1,即对角线为 1 的矩阵。
c++
#include<bits/stdc++.h>
#define int long long
#define endl putchar('\n')
#define psp putchar(' ')
using namespace std;
const int mod=1e9+7;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar();
return x*f;
}
void print(int x){
if(x<0)putchar('-'),x=-x;
if(x<10){putchar(x+'0');return;}
print(x/10);
putchar(x%10+'0');
}
void putstr(string s){
for(int i=0;i<s.size();i++)putchar(s[i]);
}
int lowbit(int x){
return x&-x;
}
int n,m,k;
int T;
int a[101][101];
int res[101][101];
int b[101][101];
void mul_res(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
b[i][j]=res[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
res[i][j]=0;
for(int l=1;l<=n;l++){
res[i][j]=(res[i][j]+a[i][l]*b[l][j]%mod)%mod;
}
}
}
}
void mul_a(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
b[i][j]=a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=0;
for(int l=1;l<=n;l++){
a[i][j]=(a[i][j]+b[i][l]*b[l][j]%mod)%mod;
}
}
}
}
signed main(){
n=read(),k=read();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
a[i][j]=read();
}
}
for(int i=1;i<=n;i++)res[i][i]=1;
while(k){
if(k&1)mul_res();
mul_a();
k>>=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
print((res[i][j]+mod)%mod),psp;
}
endl;
}
}
应用
形象化矩阵乘法
所以矩阵到底有什么用?
首先需要形象化矩阵乘法的过程。
先看一个 1×N1\times N1×N 的矩阵 AAA 乘上一个 N×1N\times 1N×1 的矩阵 BBB。
∣A1,1⋯A1,N∣×∣B1,1⋮BN,1∣ \begin{vmatrix}A_{1,1}&\cdots&A_{1,N}\end{vmatrix}\times\begin{vmatrix}B_{1,1}\\\vdots\\B_{N,1}\end{vmatrix} A1,1⋯A1,N × B1,1⋮BN,1
我们可以形象地理解为,将矩阵 AAA 竖起来,然后依次与矩阵 BBB 相乘。
如下图:

这样矩阵乘法就有了一种图像化的方法:先将第一个矩阵倒过来,再与第二个相乘。
现在解决了 (1×N)⋅(N×1)(1\times N)\cdot(N\times 1)(1×N)⋅(N×1) 的问题,接下来看 (1×N)⋅(N×M)(1\times N)\cdot(N\times M)(1×N)⋅(N×M) 的问题。

其实与单行的很类似,还是将第一个矩阵翻转过来,一列一列地计算就行了。
最后看到 (K×N)⋅(N×M)(K\times N)\cdot(N\times M)(K×N)⋅(N×M) 的计算。
我们可以将 K×NK\times NK×N 的矩阵拆分成 KKK 个 1×N1\times N1×N 的单行矩阵,这样又可以计算了。
应用 1
看一个简单的例子:计算 a+ba+ba+b。
我们构造一个矩阵 A=∣ab∣A=\begin{vmatrix}a&b\end{vmatrix}A= ab ,我们需要它们的和。
计算它们的和需要什么?需要第一个数和第二个数。
这个看似废话,实际就是废话。
然后我们看到最后的结果,a+ba+ba+b 的和是一个数,所以我们最终需要答案是一个 1×11\times 11×1 的矩阵。
构造的矩阵是 1×21\times 21×2 的,因此我们要乘上一个 2×12\times 12×1 的矩阵 BBB。
两者相乘,得到的结果是 ∣A1,1×B1,1+A1,2×B2,1∣\begin{vmatrix}A_{1,1}\times B_{1,1}+A_{1,2}\times B_{2,1}\end{vmatrix} A1,1×B1,1+A1,2×B2,1 。
将 A1,1=a,A1,2=bA_{1,1}=a,A_{1,2}=bA1,1=a,A1,2=b 带入:∣a×B1,1+b×B2,1∣\begin{vmatrix}a\times B_{1,1}+b\times B_{2,1}\end{vmatrix} a×B1,1+b×B2,1 。
我们需要的是 a+ba+ba+b,所以需要 B1,1=B2,1=1B_{1,1}=B_{2,1}=1B1,1=B2,1=1,所以 B=∣11∣B=\begin{vmatrix}1\\1\end{vmatrix}B= 11 。
应用 2
这一个应用可以推出矩阵乘法的最终奥义!
接下来的内容不着重于矩阵乘法的计算过程,如果有阅读困难请结合上文中形象化的矩阵乘法来理解。
计算斐波那契数列的第 nnn 位,但是 n≤1018n\le 10^{18}n≤1018。
我们用一个二元组 (x,y)(x,y)(x,y) 表示斐波那契数列的当前位和上一位。
如果我们要用二元组 (x,y)(x,y)(x,y) 推出下一个二元组是 (y,x+y)(y,x+y)(y,x+y)。
那么如果我们构造一个矩阵 A=∣xy∣A=\begin{vmatrix}x&y\end{vmatrix}A= xy 以表示上面二元组的信息,我们想要得到矩阵 ∣yx+y∣\begin{vmatrix}y&x+y\end{vmatrix} yx+y 。
首先,我们发现结果矩阵是 1×21\times 21×2 的,AAA 是 1×21\times 21×2 的,那么需要构造的乘数矩阵 BBB 应该是 2×22\times 22×2 的。
目前矩阵状态是这样的:
B=∣????∣ B=\begin{vmatrix}?&?\\?&?\end{vmatrix} B= ????
我们看到结果矩阵的第一位是 yyy,我们怎么用矩阵 AAA 得到 yyy?有:
y=x×0+y×1 y=x\times 0+y\times 1 y=x×0+y×1
所以我们可以得到矩阵 BBB 第一列中的数:
B=∣0?1?∣ B=\begin{vmatrix}0&?\\1&?\end{vmatrix} B= 01??
看到结果矩阵的第二个数,是 x+yx+yx+y,怎么得到?有:
x+y=x×1+y×1 x+y=x\times 1+y\times 1 x+y=x×1+y×1
所以可以得到完整的矩阵 BBB:
B=∣0111∣ B=\begin{vmatrix}0&1\\1&1\end{vmatrix} B= 0111
有:
∣xy∣×∣0111∣=∣yx+y∣ \begin{vmatrix}x&y\end{vmatrix}\times\begin{vmatrix}0&1\\1&1\end{vmatrix}=\begin{vmatrix}y&x+y\end{vmatrix} xy × 0111 = yx+y
我们构造最初的矩阵 AAA:
∣11∣ \begin{vmatrix}1&1\end{vmatrix} 11
现在我们要求斐波那契数列的第 nnn 位。我们令:
A′=A×Bn−2 A'=A\times B^{n-2} A′=A×Bn−2
此时的 A1,2′A'_{1,2}A1,2′ 就是斐波那契数列的第 nnn 位。
对于后面的 Bn−2B^{n-2}Bn−2 可以用矩阵快速幂提前计算出,由于结合律的存在,不会影响结果。
最后的时间复杂度就是 O(logn)O(\log n)O(logn)。
推广 1
矩阵乘法虽然名字里有乘法,但是也可以加速加法的递推,如斐波那契数列,其递推式子本是:
febi=febi−1+febi−2 feb_i=feb_{i-1}+feb_{i-2} febi=febi−1+febi−2
但却可以转化成矩阵乘法。
我们不妨推广一下:若有递推式子:
dpi=dpi−1+2×dpi−2−dpi−3 dp_i=dp_{i-1}+2\times dp_{i-2}-dp_{i-3} dpi=dpi−1+2×dpi−2−dpi−3
如何转化成矩阵?
我们还是可以从相邻的状态转移开始。
定义矩阵:
A=∣xyz∣ A=\begin{vmatrix}x&y&z\end{vmatrix} A= xyz
矩阵 AAA 维护的信息有 dpi,dpi−1,dpi−2dp_i,dp_{i-1},dp_{i-2}dpi,dpi−1,dpi−2。
为什么没有维护 dpi−3dp_{i-3}dpi−3?
因为我们当前状态需要的只是 dpidp_idpi,若要推下一个状态,dpidp_idpi 就成了 dpi−1dp_{i-1}dpi−1,同理,dpi−2dp_{i-2}dpi−2 就是 dpi−3dp_{i-3}dpi−3 了。
然后看到转移。我们期望的下一步状态是:
∣x+2×y−zxy∣ \begin{vmatrix}x+2\times y-z&x&y\end{vmatrix} x+2×y−zxy
所以我们需要一个 3×33\times 33×3 的转移矩阵 BBB。
首先,我们需要结果的第一位是 x+2×y−zx+2\times y-zx+2×y−z。
因此,矩阵 BBB 的第一列出来了。
B=∣1??2??−1??∣ B=\begin{vmatrix}1&?&?\\2&?&?\\-1&?&?\end{vmatrix} B= 12−1??????
然后是第二列,我们只需要 xxx,所以对应 xxx 的位是 1,其余为 0。
B=∣11?20?−10?∣ B=\begin{vmatrix}1&1&?\\2&0&?\\-1&0&?\end{vmatrix} B= 12−1100???
第三列只要 yyy,与第二列同理。
B=∣110201−100∣ B=\begin{vmatrix}1&1&0\\2&0&1\\-1&0&0\end{vmatrix} B= 12−1100010
推广 2
如果我们的式子增加了一个常数:
dpi=dpi−1+dpi−2+1 dp_i=dp_{i-1}+dp_{i-2}+1 dpi=dpi−1+dpi−2+1
这时候我们在矩阵中加一个常数就行了。
定义当前矩阵:
∣xy1∣ \begin{vmatrix}x&y&1\end{vmatrix} xy1
目标状态:
∣x+y+1x1∣ \begin{vmatrix}x+y+1&x&1\end{vmatrix} x+y+1x1
看到转移。
对于第一列,有:x+y+1=x×1+y×1+1×1x+y+1=x\times 1+y\times 1+1\times 1x+y+1=x×1+y×1+1×1。
对于第二列,有:x=x×1+y×0+1×0x=x\times 1+y\times 0+1\times 0x=x×1+y×0+1×0。
对于第三列,有:1=x×0+y×0+1×11=x\times 0+y\times 0+1\times 11=x×0+y×0+1×1。
最终矩阵:
∣110100101∣ \begin{vmatrix}1&1&0\\1&0&0\\1&0&1\end{vmatrix} 111100001
推广 3
加一个变量,有递推式子:
dpn=dpn−1+dpn−2+n dp_n=dp_{n-1}+dp_{n-2}+n dpn=dpn−1+dpn−2+n
此时我们还是可以带一个 nnn,但是还需要多带一个常数 1,帮助 nnn 变成 n+1n+1n+1。
当前状态:
∣xyn1∣ \begin{vmatrix}x&y&n&1\end{vmatrix} xyn1
目标状态:
∣x+y+nxn+11∣ \begin{vmatrix}x+y+n&x&n+1&1\end{vmatrix} x+y+nxn+11
一列一列地讨论。
第一列:x+y+n=x×1+y×1+n×1+1×0x+y+n=x\times 1+y\times 1+n\times 1+1\times 0x+y+n=x×1+y×1+n×1+1×0
第二列:x=x×1+y×0+n×0+1×0x=x\times 1+y\times 0+n\times 0+1\times 0x=x×1+y×0+n×0+1×0
第三列:n+1=x×0+y×0+n×1+1×1n+1=x\times 0+y\times 0+n\times 1+1\times 1n+1=x×0+y×0+n×1+1×1
第四列:1=x×0+y×0+n×0+1×11=x\times 0+y\times 0+n\times 0+1\times 11=x×0+y×0+n×0+1×1
最终矩阵:
∣1100100010100011∣ \begin{vmatrix}1&1&0&0\\1&0&0&0\\1&0&1&0\\0&0&1&1\end{vmatrix} 1110100000110001
推广 4
式子:
dpi=dpi−1+dpi−2 dp_i=dp_{i-1}+dp_{i-2} dpi=dpi−1+dpi−2
但是同时需要维护前缀和:
Si=Si−1+dpi S_i=S_{i-1}+dp_i Si=Si−1+dpi
我们可以将 SiS_iSi 拆分:Si−1+dpi−1+dpi−2S_{i-1}+dp_{i-1}+dp_{i-2}Si−1+dpi−1+dpi−2
为什么要拆分?因为 dpidp_idpi 是不知道的, dpi−1,dpi−2dp_{i-1},dp_{i-2}dpi−1,dpi−2 才是已知的。
当前状态:
∣xys∣ \begin{vmatrix}x&y&s\end{vmatrix} xys
目标状态:
∣x+yxs+x+y∣ \begin{vmatrix}x+y&x&s+x+y\end{vmatrix} x+yxs+x+y
第一列:x+y=x×1+y×1+s×0x+y=x\times 1+y\times 1+s\times 0x+y=x×1+y×1+s×0
第二列:x=x×1+y×0+s×0x=x\times 1+y\times 0+s\times 0x=x×1+y×0+s×0
第三列:s+x+y=x×1+y×1+s×1s+x+y=x\times 1+y\times 1+s\times 1s+x+y=x×1+y×1+s×1
最终矩阵:
∣111101001∣ \begin{vmatrix}1&1&1\\1&0&1\\0&0&1\end{vmatrix} 110100111
推广 5
式子:
dpn=dpn−1+dpn−2+(n+1)2 dp_n=dp_{n-1}+dp_{n-2}+(n+1)^2 dpn=dpn−1+dpn−2+(n+1)2
对于 (n+1)2(n+1)^2(n+1)2,有 (n+1)2=n2+2n+1(n+1)^2=n^2+2n+1(n+1)2=n2+2n+1,所以我们需要维护 n2,nn^2,nn2,n 和 1。
当前状态:
∣xyn2n1∣ \begin{vmatrix}x&y&n^2&n&1\end{vmatrix} xyn2n1
目标状态:
∣x+y+n2+2n+1x(n+1)2n+11∣ \begin{vmatrix}x+y+n^2+2n+1&x&(n+1)^2&n+1&1\end{vmatrix} x+y+n2+2n+1x(n+1)2n+11
第一列:x+y+n2+2n+1=x×1+y×1+n2×1+n×2+1×1x+y+n^2+2n+1=x\times 1+y\times 1+n^2\times 1+n\times 2+1\times 1x+y+n2+2n+1=x×1+y×1+n2×1+n×2+1×1
第二列:x=x×1+y×0+n2×0+n×0+1×0x=x\times 1+y\times 0+n^2\times 0+n\times 0+1\times 0x=x×1+y×0+n2×0+n×0+1×0
第三列:(n+1)2=n2+2n+1=x×0+y×0+n2×1+n×2+1×1(n+1)^2=n^2+2n+1=x\times 0+y\times 0+n^2\times 1+n\times 2+1\times 1(n+1)2=n2+2n+1=x×0+y×0+n2×1+n×2+1×1
第四列:n+1=x×0+y×0+n2×0+n×1+1×1n+1=x\times 0+y\times 0+n^2\times 0+n\times 1+1\times 1n+1=x×0+y×0+n2×0+n×1+1×1
第五列:1=x×0+y×0+n2×0+n×0+1×11=x\times 0+y\times 0+n^2\times 0+n\times 0+1\times 11=x×0+y×0+n2×0+n×0+1×1
最终矩阵:
∣1100010000101002021010111∣ \begin{vmatrix}1&1&0&0&0\\1&0&0&0&0\\1&0&1&0&0\\2&0&2&1&0\\1&0&1&1&1\end{vmatrix} 1112110000001210001100001
总结
矩阵乘法优化递推时,我们用一个 1×N1\times N1×N 的矩阵表示需要用的状态,如 dpi,dpi−1,dpi−2⋯dp_i,dp_{i-1},dp_{i-2}\cdotsdpi,dpi−1,dpi−2⋯。
构造转移矩阵是比较难想,但是我们可以从最终的结果推,就像走迷宫从终点开始一样。
我们有一个 1×N1\times N1×N 的矩阵表示当前状态,还有一个 1×N1\times N1×N 的矩阵表示下一步的目标状态,有了这两个矩阵,就很容易推出中间 N×NN\times NN×N 的转移矩阵了。