0%

矩阵总结

矩阵是个很神奇的东西,可惜我涉猎较少,只能浅谈矩阵的应用。

我们先定义几个符号:

  • A:矩阵 A
  • Ai:矩阵 A i
  • Ai,j:矩阵 A i 行第 j 列的元素
  • Mi,j:矩阵 A 去掉第 i 行和第 j 列的矩阵(余子式)
  • AT:矩阵 A 的转置
  • An:矩阵 A n 次幂
  • det(A):矩阵 A 的行列式

矩阵乘法

定义矩阵乘法 A×B=C,Ci,j=Σk=1mAi,kBk,j,其中 A 是 $n mBmp$ 列的矩阵。矩阵乘法不满足交换率,但满足结合率分配率

在这种定义下,矩阵乘法满足交换率的情况:

  • 两矩阵相等
  • 两矩阵至少有一个为数量矩阵(主对角线为相同的数,其他为 0
  • 两矩阵的乘积等于两矩阵的和(了解)

矩阵的幂

根据矩阵乘法的定义,只有 n n 列的矩阵(方阵)才有幂。

矩阵满足结合率,因此可以使用快速幂加速,计算大小为 n 的矩阵 Ak 时间复杂度 O(n3logk)。如果加速矩阵乘法,可以降低复杂度,但在 OI 中这个复杂度就足够了。

利用矩阵的幂可以求线性递推式(如 Fibonacci)。

邻接矩阵性质

假设 A n 个节点的图 G 的邻接矩阵,则考虑一个矩阵乘法的过程:

Gi,j2=Σk=1nGi,kGk,j

相当于枚举经过的 k 点,计算从 i j 经过 2 条边的方案数。这个过程其实就是在模拟 Floyd。因此我们猜测:Gi,jk 表示经过 k 条边 (k0),从 i 到达 j 的方案数。事实上这是正确的。

如果我们重新定义矩阵乘法 A×B=C,Ci,j=min1kn{Ai,k+Bk,j},则我们发现 G2 就是 Floyd,Gk 是一张经过 k 条边的最短路邻接矩阵。

这提示我们可以根据需要重新定义矩阵乘法,但是仍要保证新的定义满足结合率(不用考虑交换率是因为乘数相同已经满足了交换率)。

高斯消元

其实是求解一个线性增广矩阵。

假定有列数为 n 的矩阵 A 和大小为 n 的向量 B,高斯消元可以解得满足 A×X=B 的向量 X。更一般地说,高斯消元可解 Ai×Xi=Bi 的解向量 X

高斯消元的理论在很多资料和训练指南上都有,因此此处重点分析代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void GausElim(){
for(int i=0;i<n;i++){
int cur=i;
for(int j=i+1;j<n;j++)
if(fabs(a[j][i])>fabs(a[cur][i]))cur=j;
for(int j=0;j<=n;j++)swap(a[i][j],a[cur][j]);

for(int j=i+1;j<n;j++)
for(int k=n;k>=i;k--)
a[j][k]-=a[i][k]*a[j][i]/a[i][i];
}
for(int i=n-1;i>=0;i--){
for(int j=i+1;j<n;j++)
a[i][n]-=a[j][n]*a[i][j];
a[i][n]/=a[i][i];
}
}

增广矩阵 A 的最后一列即为解向量。流程如下:

  1. 对项 i 找到系数绝对值最大的一行并交换(减小精度误差)
  2. i 行之后的每一行 j 的每个元素 Aj,k 都减去 Aj,k×Aj,iAi,i。为了不破坏 Aj,i,需要逆序进行;
  3. 对每一行都操作完毕后,逆序将已有的解代入当前方程组求解。

注意上面的求法是只有唯一解时的求法。如果枚举到某行的某一项 i 发现后面所有项 i 的系数都为 0,则 i自由元(取值任意),并在该行考察下一项 i+1。如果解完后 i 行的系数全为 0 Ai,n0,则原方程组无解

模意义下的高斯消元

边做边模即可。注意因为取模,有可能会出现多解的情况。但是此时若有多解,解的个数是可数的,因此我们往往需要求解的个数。

给出结论:在模 p 意义下解方程组若有 n 个自由元,则方程组的解个数为 pn

因此像上述一样统计自由元就好了。但是我们很担心 p 不是质数,否则我们做除法的时候逆元不一定存在。这里就有个神奇小技巧了。

辗转相除法求逆

假设我们要求 AiAiAjAi,iAj,i。方便描述,我们令 a=Ai,i,b=Aj,i,然后:

  1. c=abAiAicAj
  2. aamodb,交换 a,b,交换 Ai,Aj
  3. b 不为 0 时重复 1。

原理未知。但看起来过程是扩展欧几里得(上面的过程极有可能是的)。

异或方程组

2 意义下的高斯消元,相当于采用异或运算。

矩阵求逆

不会。

行列式

定义函数 sign(p) 为全排列 p 中的逆序对数。对于方阵 A,其行列式 detA 定义:

det(A)=Σppermutation[(1)sign(p)Πi=1nAi,pi]

看起来就不好求。但是我们有性质:

  1. det(A)=det(AT)
  2. det(A)=det(Aswap(i,j))
  3. kdet(a)=detAAi=kAi
  4. Ai=kAj,则 det(A)=0
  5. A=B+C,则 det(A)=det(B)+det(C)
  6. det(A)=det(AAi=Ai+kAj)

具体的证明和文字说明见这里。利用性质六,我们就可以进行高斯消元了。

具体步骤和高斯消元一样。根据性质二,交换两行需要乘 1。根据性质六,我们消元时应该用第 j 行减去第 i 行作为第 j 行的答案,而不能用第 i 行减去第 j 行。

消元完毕后,答案即为主对角线的乘积。因为不选主对角线上的数必定会选中 0,对答案没有贡献。而主对角线的排列 p 逆序对数为 0。这样我们就在 O(n3) 的时间内求出矩阵的行列式。

矩阵树定理

无向图 G 的生成树个数。根据矩阵树定理(Matrix-Tree Theorem/Kirchhoff's Theorem),若 G 的 Kirchhoff 矩阵为 K,则其生成树个数 ST(G) 为:

ST(G)=detKi,i

i 的值无所谓。具体证明和推导见这里

然而什么是 Kirchhoff 矩阵?令图 G 的度数矩阵为 D,邻接矩阵为 A,则 K=DA。更一般地,有:

Ki,j={deg(i),i=j1,(i,j)E0,otherwise

但是这样高斯消元容易有精度误差,解决方法未知,可以考虑分数类。不过一般都是在模意义下的高斯消元,然后就可以采用辗转相除的黑科技保证精度。

可以扩展到有向图上,求出来的是有向树。

特征值及特征多项式

哈?

练习

矩阵乘法

BZOJ3444 ⑨的故事

题意:求 A9Σi=1nAi

由于矩阵乘法满足分配率,因此当 n 为偶数时有 Σi=1nAi=(1+An/2)Σi=1n/2Ai,奇数的情况相同考虑。然后就可以分治了。

时间复杂度 O(n3logn)

矩阵快速幂

HNOI2002 公交车路线

我们能得到递推方程

fi,j={0,j=4infi1,j1+fi1,j+1,otherwise

其中 fi,j 表示坐 i 次车到达 j 站的方案数。

这个式子线性递推且大小只有 8,矩阵快速幂。

USACO2007NovGold Cow Relays

经过 k 条边的最短路径。注意到边数 nE 不大且每个点度数 2,因此只需要最多 2nE 个点即可,离散化。

ZJOI2004 鳄鱼沼泽

仍然是求经过 k 条边的路径方案数,不过路径会变。然而变化周期为 2,3,4,因此大周期为 (2,3,4)=12 就能出现循环。假设第 i 个局面的前缀积矩阵为 Gi,则大循环矩阵为 G12,答案矩阵为 G12n12Gnmod12

注意修改矩阵的时候,若 u 不能通过,应该将前一个图到达 u 的点和该图从 u 出发的点置 0 才对。

BSOJ3792 做梦

fi i 时间后在家中的方案数,gi i 时间内从家出发走 i 步不经过家且最后在家的方案数。则 fi=Σj=2mfijgj。发现这个形式线性相关(gj 可以看作常数),构造矩阵: [g2g4gm2gm100001000010][fi2fi4fi6fim]=[fifi2fi4fim+2] 前面的方阵可以快速幂。手推(打表)一下 g 就发现是 Catalan 数。

Codeforces719E Sasha and Array

题意:要求支持两种操作:区间 Fibonacci 和,区间加法。

好题。明显线段树,然而线段树怎么维护值呢。考虑到 Fibonacci 可以用矩阵快速幂计算,因此我们可以给线段树每个节点都丢一个 Fibonacci 变换矩阵 G

不难发现对 Fibonacci 数列 f(n) 有: f(n+Δ)=Gn+Δ=GnGΔ=f(n)GΔ 因此修改值等价于乘变换矩阵的 Δ 次方。又矩阵乘法满足分配率:

Σi(f(ni)GΔ)=GΔΣif(ni)

因此对一个区间乘上 GΔ 等于对区间和乘上 GΔ。然后就可以用线段树愉快地维护 lazy 啦。要注意传 Δ 的时候最好计算好矩阵再传引用,否则时间开销很大。

高斯消元

JSOI2008 球形空间产生器

假设球心坐标,通过球心到 n+1 个点的距离相同可以得到 n+1 个方程,且方程右边的数相同。

相邻两个方程分别相减得到 n n 元一次方程(虽然可以得到更多方程,但是是多余的),用高斯消元即可。

BSOJ4544 开关问题

不难发现一个开关要么被开,要么不被开(开两次即以上相当于模 2 下的操作),且状态与开关顺序无关。因此我们假设每个开关是否被开,则对灯 i 有一个线性方程组,方程组左边为影响 i 的开关,方程组右边为灯的末状态和初状态的差值。解异或方程组求自由元个数即可。

矩阵树定理

SPOJ104 Highways

直接运用定理和高斯消元即可。

HEOI2015 小 Z 的房间

建图求生成树。然而要模 109,只能用辗转相除的高斯消元。

文艺计算姬

题意:求二分图 (n,m) 的生成树个数。

矩阵长这样。

[m001110m011100m111111n001110n011100n]

我们只要 Mn+m,n+m,然后消前 n 行。 D=|m001110m011100m111000nnmnmnm000nmnnmnm000nmnmnnm| D1 D2 有: D1=|m000m000m|,D2=|nnmnmnmnmnnmnmnmnmnnm| 由于左下角都为 0,因此它们对行列式没有贡献,有贡献的只有左上角和右下角,即 D=D1D2。显然 D1=mn。我们观察 D2

把除了第一行的行都加到第一行上。 D2=|nmnmnmnmnnmnmnmnmnnm| 提出第一行的常数: D2=nm|111nmnnmnmnmnmnnm| 把第一行乘 nm 加到下面: D2=nm|1110n000n|

显然 D2=nmnm2=nm1m1。因此 D=D1D2=nm1mn1

另外这道题也可以用 prufer 序列得到式子:

// 有空再写

需要快速乘和快速幂。

总结

矩阵是线性代数的基础之一,其拥有很多性质,如快速幂给我们一种计算线性递推式的方法,行列式可以进行生成树计数。(虽然还有很多性质没有学习)

  • n 项的线性递推式可以构造一个 n×n 的变换矩阵进行快速幂。如果项中有不能直接从上一次的元素中得到的项(如 k 次幂),需要将该项的递推式项也加入矩阵,否则可以直接在变换矩阵里写一个常数转移。

  • 邻接矩阵的 k 次幂是一个经过 k 条边的方案数矩阵。如果有必要,可以重新定义乘法;

  • 高斯消元有多解时,若某项及之后的式子该项系数全为 0,则需要保持在该行并考察下一项。

  • 行列式求值时,交换两行需要乘 1。且消元时应该用第 j 行减去第 i 行作为第 j 行的答案。

  • 行列式的六个性质:

    1. det(A)=det(AT)
    2. det(A)=det(Aswap(i,j))
    3. kdet(a)=detAAi=kAi
    4. Ai=kAj,则 det(A)=0
    5. A=B+C,则 det(A)=det(B)+det(C)
    6. det(A)=det(AAi=Ai+kAj)
  • 进行行列式化简时,若每一行都刚好有一个元素相同且互不同列,而其他元素也相同,则可以把所有行都加在一行上得到一行相同的数,提出常数得到 1,再依次消元。