用途
矩阵树一般用于生成树计数的问题,比如求一个无向图中生成树的个数。用矩阵树定理能极大地降低时间复杂度。
前置知识:行列式
此部分可粗略浏览,了解即可。
对于一个一阶行列式,可写作
d
e
t
(
a
1
,
1
)
=
a
1
,
1
det \left( \begin{matrix} a_{1,1} \end{matrix} \right)=a_{1,1}
det(a1,1)=a1,1
对于一个二阶行列式,可写作
d
e
t
(
a
1
,
1
a
1
,
2
a
2
,
1
a
2
,
2
)
=
a
1
,
1
a
2
,
2
−
a
1
,
2
a
2
,
1
det \left( \begin{matrix} a_{1,1} & a_{1,2}\\ a_{2,1} & a_{2,2} \end{matrix} \right)=a_{1,1}a_{2,2}-a{1,2}a_{2,1}
det(a1,1a2,1a1,2a2,2)=a1,1a2,2−a1,2a2,1
对于一个三阶的行列式,可写作
d
e
t
(
a
1
,
1
a
1
,
2
a
1
,
3
a
2
,
1
a
2
,
2
a
2
,
3
a
3
,
1
a
3
,
2
a
3
,
3
)
=
a
1
,
1
a
2
,
2
a
3
,
3
+
a
1
,
2
a
2
,
3
a
3
,
1
+
a
1
,
3
a
2
,
1
a
3
,
2
−
a
1
,
3
a
2
,
2
a
3
,
1
−
a
1
,
2
a
2
,
1
a
3
,
3
−
a
1
,
1
a
2
,
3
a
3
,
2
det \left( \begin{matrix} a_{1,1} & a_{1,2} & a_{1,3}\\ a_{2,1} & a_{2,2} & a_{2,3}\\ a_{3,1} & a_{3,2} & a_{3,3} \end{matrix} \right)=a_{1,1}a_{2,2}a_{3,3}+a_{1,2}a_{2,3}a_{3,1}+a_{1,3}a_{2,1}a_{3,2}\\ \qquad\qquad \qquad \qquad\qquad\qquad-a_{1,3}a_{2,2}a_{3,1}-a_{1,2}a_{2,1}a_{3,3}-a_{1,1}a_{2,3}a_{3,2}
det⎝⎛a1,1a2,1a3,1a1,2a2,2a3,2a1,3a2,3a3,3⎠⎞=a1,1a2,2a3,3+a1,2a2,3a3,1+a1,3a2,1a3,2−a1,3a2,2a3,1−a1,2a2,1a3,3−a1,1a2,3a3,2
通过观察,我们得到 n n n阶行列式的一般定义。
令 p 1 , p 2 , … , p n p_1,p_2,\dots,p_n p1,p2,…,pn表示一个 1 1 1到 n n n的排列。若一个排列的逆序对数个数为偶数,则称这个排列为偶排列;若一个排列的逆序对数个数为奇数,则称这个排列为奇排列。
令
t
(
p
1
,
p
2
,
…
,
p
n
)
=
{
0
,
当
p
1
,
p
2
,
…
,
p
n
是
偶
排
列
时
1
,
当
p
1
,
p
2
,
…
,
p
n
是
奇
排
列
时
t(p_1,p_2,\dots,p_n)= \begin{cases} 0,\quad 当p_1,p_2,\dots,p_n是偶排列时\\ 1,\quad 当p_1,p_2,\dots,p_n是奇排列时 \end{cases}
t(p1,p2,…,pn)={0,当p1,p2,…,pn是偶排列时1,当p1,p2,…,pn是奇排列时
则
n
n
n阶行列式可以写成
d
e
t
(
a
1
,
1
a
1
,
2
⋯
a
1
,
n
a
2
,
1
a
2
,
2
⋯
a
2
,
n
⋮
⋮
⋱
⋮
a
n
,
1
a
n
,
2
⋯
a
n
,
n
)
=
∑
p
1
,
p
2
,
…
p
n
(
−
1
)
t
(
p
1
,
p
2
,
…
,
p
n
)
a
1
,
p
1
a
2
,
p
2
⋯
a
n
,
p
n
det \left( \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right) =\sum\limits_{p_1,p_2,\dots p_n}(-1)^{t(p_1,p_2,\dots,p_n)}a_{1,p_1}a_{2,p_2}\cdots a_{n,p_n}
det⎝⎜⎜⎜⎛a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n⎠⎟⎟⎟⎞=p1,p2,…pn∑(−1)t(p1,p2,…,pn)a1,p1a2,p2⋯an,pn
行列式也可写作
d
e
t
A
=
d
e
t
(
a
1
,
1
a
1
,
2
⋯
a
1
,
n
a
2
,
1
a
2
,
2
⋯
a
2
,
n
⋮
⋮
⋱
⋮
a
n
,
1
a
n
,
2
⋯
a
n
,
n
)
=
∣
a
1
,
1
a
1
,
2
⋯
a
1
,
n
a
2
,
1
a
2
,
2
⋯
a
2
,
n
⋮
⋮
⋱
⋮
a
n
,
1
a
n
,
2
⋯
a
n
,
n
∣
=
∣
A
∣
det A= det \left( \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right)= \left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= |A|
detA=det⎝⎜⎜⎜⎛a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n⎠⎟⎟⎟⎞=∣∣∣∣∣∣∣∣∣a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣=∣A∣
行列式的性质
性质1
设 A A A是 n n n阶矩阵,则 d e t A T = d e t A detA^T=detA detAT=detA
性质2
行列式任意两行(或两列)互换,行列式变号。
推论: 行列式某两行(或两列)相同时,行列式值为 0 0 0。
性质3
将一行(或一列)的每个数乘实数 k k k,得到的行列式等于原来的行列式乘 k k k。
推论: 行列式有两行(或两列)成比例,则行列式值为 0 0 0。特别地,当行列式有一行(或一列)全为 0 0 0时,行列式值为 0 0 0。
性质4
∣ a 1 , 1 + b 1 , 1 a 1 , 2 ⋯ a 1 , n a 2 , 1 + b 2 , 1 a 2 , 2 ⋯ a 2 , n ⋮ ⋮ ⋱ ⋮ a n , 1 + b n , 1 a n , 2 ⋯ a n , n ∣ = ∣ a 1 , 1 a 1 , 2 ⋯ a 1 , n a 2 , 1 a 2 , 2 ⋯ a 2 , n ⋮ ⋮ ⋱ ⋮ a n , 1 a n , 2 ⋯ a n , n ∣ + ∣ b 1 , 1 a 1 , 2 ⋯ a 1 , n b 2 , 1 a 2 , 2 ⋯ a 2 , n ⋮ ⋮ ⋱ ⋮ b n , 1 a n , 2 ⋯ a n , n ∣ \left| \begin{matrix} a_{1,1}+b_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1}+b_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1}+b_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= \left| \begin{matrix} a_{1,1}& a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|+ \left| \begin{matrix} b_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ b_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ b_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right| ∣∣∣∣∣∣∣∣∣a1,1+b1,1a2,1+b2,1⋮an,1+bn,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣=∣∣∣∣∣∣∣∣∣a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣+∣∣∣∣∣∣∣∣∣b1,1b2,1⋮bn,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣
对列也相同。
推论: 将行列式任意一行(或一列)乘一个实数 k k k再加到另一行(或另一列)上,行列式的值不变。
∣ a 1 , 1 a 1 , 2 ⋯ a 1 , n a 2 , 1 a 2 , 2 ⋯ a 2 , n ⋮ ⋮ ⋱ ⋮ a n , 1 a n , 2 ⋯ a n , n ∣ = ∣ a 1 , 1 + k ⋅ a j , 1 a 1 , 2 + k ⋅ a j , 2 ⋯ a 1 , n + k ⋅ a j , n a 2 , 1 a 2 , 2 ⋯ a 2 , n ⋮ ⋮ ⋱ ⋮ a n , 1 a n , 2 ⋯ a n , n ∣ \left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= \left| \begin{matrix} a_{1,1}+k\cdot a_{j,1} & a_{1,2}+k\cdot a_{j,2} & \cdots & a_{1,n}+k\cdot a_{j,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right| ∣∣∣∣∣∣∣∣∣a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣=∣∣∣∣∣∣∣∣∣a1,1+k⋅aj,1a2,1⋮an,1a1,2+k⋅aj,2a2,2⋮an,2⋯⋯⋱⋯a1,n+k⋅aj,na2,n⋮an,n∣∣∣∣∣∣∣∣∣
行列式的计算方法
对于一个行列式
d
e
t
A
=
∣
a
1
,
1
a
1
,
2
⋯
a
1
,
n
a
2
,
1
a
2
,
2
⋯
a
2
,
n
⋮
⋮
⋱
⋮
a
n
,
1
a
n
,
2
⋯
a
n
,
n
∣
detA= \left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|
detA=∣∣∣∣∣∣∣∣∣a1,1a2,1⋮an,1a1,2a2,2⋮an,2⋯⋯⋱⋯a1,na2,n⋮an,n∣∣∣∣∣∣∣∣∣
通过上述性质可将其变为上三角形式
d
e
t
B
=
∣
b
1
,
1
b
1
,
2
⋯
b
1
,
n
0
b
2
,
2
⋯
b
2
,
n
⋮
⋮
⋱
⋮
0
0
⋯
b
n
,
n
∣
detB= \left| \begin{matrix} b_{1,1} & b_{1,2} & \cdots & b_{1,n}\\ 0 & b_{2,2} & \cdots & b_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & \cdots & b_{n,n} \end{matrix} \right|
detB=∣∣∣∣∣∣∣∣∣b1,10⋮0b1,2b2,2⋮0⋯⋯⋱⋯b1,nb2,n⋮bn,n∣∣∣∣∣∣∣∣∣
设变化过程中交换行(或列)的次数为 s s s,则 d e t A = d e t B = ( − 1 ) s ∏ i = 1 n b i , i detA=detB=(-1)^s\prod\limits_{i=1}^nb_{i,i} detA=detB=(−1)si=1∏nbi,i
也就是说,我们可以通过类似高斯消元的方式,用 O ( n 3 ) O(n^3) O(n3)的时间复杂度来求一个 n n n阶行列式的值。
矩阵树定理
有 n ( 1 ≤ n ≤ 12 ) n(1\leq n\leq 12) n(1≤n≤12)个点,有一些点可以连边。求有多少种连边方案,可以使这 n n n个点形成一棵树。
矩阵树定理介绍
对于一个无向图 G G G,有以下几个概念:
- G G G的度数矩阵 D [ G ] D[G] D[G]是一个 n ∗ n n*n n∗n的矩阵,且满足 d i , j = { 0 i ≠ j 点 i 的 度 数 i = j d_{i,j}= \begin{cases} 0 \qquad\qquad\quad i\neq j\\ 点i的度数 \quad i=j\quad \end{cases} di,j={0i=j点i的度数i=j
- G G G的邻接矩阵 A [ G ] A[G] A[G]也是一个 n ∗ n n*n n∗n的矩阵,且满足 a i , j = { 1 点 i 和 点 j 之 间 有 边 相 连 0 点 i 和 点 j 之 间 没 有 边 相 连 a_{i,j}= \begin{cases} 1 \quad 点i和点j之间有边相连\\ 0 \quad 点i和点j之间没有边相连\quad \end{cases} ai,j={1点i和点j之间有边相连0点i和点j之间没有边相连
我们定义 G G G的 K i r c h h o f f Kirchhoff Kirchhoff矩阵 C [ G ] C[G] C[G]为 C [ G ] = D [ G ] − A [ G ] C[G]=D[G]-A[G] C[G]=D[G]−A[G],则 G G G所有不同生成树的个数等于其 K i r c h h o f f Kirchhoff Kirchhoff矩阵 C [ G ] C[G] C[G]的任意一个 n − 1 n-1 n−1阶主子式的行列式的绝对值。
n − 1 n-1 n−1阶主子式: 对于 r ( 1 ≤ r ≤ n ) r(1\leq r\leq n) r(1≤r≤n),将 C [ G ] C[G] C[G]的第 r r r行和第 r r r列同时去掉后得到的新矩阵,表示为 C r [ G ] C_r[G] Cr[G]。
那么对于上面这道题,我们只需要求出其 K i r c h h o f f Kirchhoff Kirchhoff矩阵 C [ G ] C[G] C[G],再求 d e t C [ G ] det C[G] detC[G]的任意一个 n − 1 n-1 n−1阶主子式的行列式的绝对值即可。
举例
一下是一个 5 5 5个点, 6 6 6条边的无向图。
则其
K
i
r
c
h
h
o
f
f
Kirchhoff
Kirchhoff矩阵为
C
[
G
]
=
∣
2
−
1
−
1
0
0
−
1
2
0
−
1
0
−
1
0
3
−
1
−
1
0
−
1
−
1
3
−
1
0
0
−
1
−
1
2
∣
C[G]= \left| \begin{matrix} 2 & -1 & -1 & 0 & 0\\ -1 & 2 & 0 & -1 & 0\\ -1 & 0 & 3 & -1 & -1\\ 0 & -1 & -1 & 3 & -1\\ 0 & 0 & -1 & -1 & 2\\ \end{matrix} \right|
C[G]=∣∣∣∣∣∣∣∣∣∣2−1−100−120−10−103−1−10−1−13−100−1−12∣∣∣∣∣∣∣∣∣∣
r
=
2
r=2
r=2时
C
2
[
G
]
=
∣
2
−
1
0
0
−
1
3
−
1
−
1
0
−
1
3
−
1
0
−
1
−
1
2
∣
=
2
⋅
∣
3
−
1
−
1
−
1
3
−
1
−
1
−
1
2
∣
−
(
−
1
)
⋅
∣
−
1
0
0
−
1
3
−
1
−
1
−
1
2
∣
=
2
×
8
+
1
×
(
−
5
)
=
11
C_2[G]= \left| \begin{matrix} 2 & -1 & 0 & 0\\ -1 & 3 & -1 & -1\\ 0 & -1 & 3 & -1\\ 0 & -1 & -1 & 2\\ \end{matrix} \right| \\ \\ =2\cdot \left| \begin{matrix} 3 & -1 & -1\\ -1 & 3 & -1\\ -1 & -1 & 2\\ \end{matrix} \right|-(-1)\cdot \left| \begin{matrix} -1 & 0 & 0\\ -1 & 3 & -1\\ -1 & -1 & 2\\ \end{matrix} \right|= 2\times 8+1\times(-5)=11
C2[G]=∣∣∣∣∣∣∣∣2−100−13−1−10−13−10−1−12∣∣∣∣∣∣∣∣=2⋅∣∣∣∣∣∣3−1−1−13−1−1−12∣∣∣∣∣∣−(−1)⋅∣∣∣∣∣∣−1−1−103−10−12∣∣∣∣∣∣=2×8+1×(−5)=11
上图生成树的个数就是 11 11 11个。
我们可以用类似高斯消元的方法来求行列式的值具体操作见下面例题的代码。其时间复杂度为 O ( n 3 ) O(n^3) O(n3)。
证明过程比较繁琐,这里不做记录。并且在做题过程中也不需要证明,有了解,学会运用即可。
例题
SPOJ104 Highways
题目大意
给你一个有
n
n
n个点
m
m
m条边的无向图,求使每两个点间有且只有一条路线有多少方案(即求生成树的个数)。
1
≤
n
≤
12
1\leq n\leq 12
1≤n≤12,有多组数据。
题解
这就是一道矩阵树定理的板题。在代码中, g a u s s ( ) gauss() gauss()函数其实就是类似高斯消元的函数,利用辗转相除法,用 O ( n log n ) O(n\log n) O(nlogn)的时间复杂度来使第 j j j行的第 i i i列的数变为 0 0 0。因为还要枚举当前行 i i i和需要修改的行 j j j,所以总时间复杂度为 O ( t ⋅ n 3 log n ) O(t\cdot n^3\log n) O(t⋅n3logn),对于这道题来说是可以过的。
code
#include<bits/stdc++.h>
using namespace std;
int t,n,m,x,y;
long long ans,a[105][105];
void gauss(){
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
while(a[j][i]){
long long tp=a[i][i]/a[j][i];
for(int v=i;v<=n;v++) a[i][v]-=a[j][v]*tp;
for(int v=i;v<=n;v++) swap(a[i][v],a[j][v]);
}
}
}
}
int main()
{
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=0;i<=100;i++){
for(int j=0;j<=100;j++) a[i][j]=0;
}
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
--a[x][y];--a[y][x];
++a[x][x];++a[y][y];
}
for(int i=1;i<=n;i++){
a[i][n]=a[n][i]=0;
}
--n;
gauss();
ans=1;
for(int i=1;i<=n;i++){
ans=ans*a[i][i];
}
if(ans<0) ans=-ans;
printf("%lld\n",ans);
}
return 0;
}