「算法笔记」矩阵树定理

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


定义

基尔霍夫矩阵

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

我们记基尔霍夫矩阵为 $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;
}

习题

发表评论