「算法笔记」矩阵树定理

矩阵树定理用于计算无向图生成树个数,和基尔霍夫矩阵的行列式密切相关。

定义

基尔霍夫矩阵

在了解矩阵树定理前,我们先学习一下基尔霍夫矩阵的求法。

我们记基尔霍夫矩阵为 $K​$($\text{Kirchhoff}​$ 的缩写),并直接计算出无向图 $G​$ 的度数矩阵 $D​$ 和邻接矩阵 $A​$,那么我们同通过 $K=D-A​$ 就可以计算出基尔霍夫矩阵。

主子式

取出矩阵 $A$ 的 $k$ 行和 $k$ 列组成的新矩阵 $A’$ 叫做 $A$ 的 $k$ 阶主子式。


矩阵树定理

用途

矩阵树定理用于求解一个无向图的生成数个数,允许有重边和自环

定理

一个 $n​$ 个点的无向图的的生成树个数等于其基尔霍夫矩阵的任何一个 $n-1​$ 阶主子式的行列式。

证明

由于笔者能力有限(太菜了不会证明),这里推荐一篇博客:生成树计数问题——矩阵树定理及其证明 - WerKeyTom_FTD

求解

构造基尔霍夫矩阵,并求出其任何一个 $n-1$ 阶主子式的行列式即可。行列式的具体求法详见「算法笔记」行列式

时间复杂度:$O(n^3\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
#include <cstdio>
#include <algorithm>

const int N=305;
const int mod=1e9+7;
int n,m,a[N][N];

int Gauss(int n) {
int ans=1;
for(int i=1;i<=n;++i) {
for(int k=i+1;k<=n;++k) {
while(a[k][i]) {
int d=a[i][i]/a[k][i];
for(int j=i;j<=n;++j) a[i][j]=(a[i][j]-1LL*d*a[k][j]%mod+mod)%mod;
std::swap(a[i],a[k]),ans=-ans;
}
}
ans=1LL*ans*a[i][i]%mod,ans=(ans+mod)%mod;
}
return ans;
}
int main() {
scanf("%d%d",&n,&m);
while(m--) {
int u,v;
scanf("%d%d",&u,&v);
--a[u][v],--a[v][u],++a[u][u],++a[v][v];
}
printf("%d\n",Gauss(n-1));
return 0;
}

习题

0%