矩阵树定理用于计算无向图生成树个数,和基尔霍夫矩阵的行列式密切相关。
定义
基尔霍夫矩阵
在了解矩阵树定理前,我们先学习一下基尔霍夫矩阵的求法。
我们记基尔霍夫矩阵为 $K$($\text{Kirchhoff}$ 的缩写),并直接计算出无向图 $G$ 的度数矩阵 $D$ 和邻接矩阵 $A$,那么我们同通过 $K=D-A$ 就可以计算出基尔霍夫矩阵。
主子式
取出矩阵 $A$ 的 $k$ 行和 $k$ 列组成的新矩阵 $A'$ 叫做 $A$ 的 $k$ 阶主子式。
矩阵树定理
用途
矩阵树定理用于求解一个无向图的生成数个数,允许有重边和自环。
定理
一个 $n$ 个点的无向图的的生成树个数等于其基尔霍夫矩阵的任何一个 $n-1$ 阶主子式的行列式。
证明
由于笔者能力有限(太菜了不会证明),这里推荐一篇博客:生成树计数问题——矩阵树定理及其证明 - WerKeyTom_FTD
求解
构造基尔霍夫矩阵,并求出其任何一个 $n-1$ 阶主子式的行列式即可。行列式的具体求法详见「算法笔记」行列式。
时间复杂度:$\mathcal O(n^3\log n)$
代码
#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 = mod - ans;
}
}
ans = 1LL * ans * a[i][i] % mod;
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
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;
}
1 条评论
%SY