矩阵树定理

矩阵树定理

矩阵树定理也称Matrix-Tree定理或Kirchhoff定理。这个定理提供了一种方式使用一个特殊的矩阵的行列式来计算一个图的生成树的数量。

对于一个无向图来说,我们可以构造它的Laplace矩阵 L L L,其中:

  • 如果节点 i i i有度为 d d d,那么 L [ i , i ] = d L[i,i] = d L[i,i]=d
  • 如果节点 i i i和节点 j j j之间有边相连,那么 L [ i , j ] = L [ j , i ] = − 1 L[i,j] = L[j,i] =-1 L[i,j]=L[j,i]=1
  • 其他情况 L [ i , j ] = 0 L[i,j] = 0 L[i,j]=0

例如下图的Laplace矩阵:

Graph

L = [ 3 − 1 − 1 − 1 − 1 1 0 0 − 1 0 2 − 1 − 1 0 − 1 2 ] L = \begin{bmatrix} 3 & -1 & -1 & -1 \\ -1 & 1 & 0 & 0 \\ -1 & 0 & 2 & -1 \\ -1 & 0 & -1 & 2 \\ \end{bmatrix} L=3111110010211012

那么我们从 L L L中任意删除某一行和某一列剩下的矩阵,对其求行列式,例如删除第一行和第一列:

det ⁡ ( [ 1 0 0 0 2 − 1 0 − 1 2 ] ) = 3 \det(\begin{bmatrix} 1 & 0 & 0 \\ 0 & 2 & -1 \\ 0 & -1 & 2 \\ \end{bmatrix}) = 3 det(100021012)=3

那么这个图的生成树的数量为 3 3 3,可以证明删除任意行列的行列式的值不变。

注意,Cayley定理是矩阵树定理的一个特殊情况,因为Cayley定理说明,一个 n n n个节点的组成树的数量为 n n − 2 n^{n-2} nn2这正好是 n n n个节点完全图的生成树的数量,因为:

L = [ n − 1 − 1 ⋯ − 1 − 1 n − 1 ⋯ − 1 ⋮ ⋮ ⋱ ⋮ − 1 − 1 ⋯ n − 1 ] L=\begin{bmatrix} n-1 & -1 & \cdots & -1 \\ -1 & n-1 & \cdots & -1 \\ \vdots & \vdots & \ddots & \vdots \\ -1 & -1 & \cdots & n-1 \\ \end{bmatrix} L=n1111n1111n1

去掉第一行第一列之后:

det ⁡ ( [ n − 1 ⋯ − 1 ⋮ ⋱ ⋮ − 1 ⋯ n − 1 ] ) = n n − 2 \det(\begin{bmatrix} n-1 & \cdots & -1 \\ \vdots & \ddots & \vdots \\ -1 & \cdots & n-1 \\ \end{bmatrix}) = n^{n-2} det(n111n1)=nn2

例题

P4111 小 Z 的房间

#include <bits/stdc++.h>

using namespace std;

#define FR freopen("in.txt", "r", stdin)
#define FW freopen("out.txt", "w", stdout)

typedef long long ll;

int mp[15][15];

ll laplace[100][100];

int n, m;

int pos[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

int tot = 1;

const ll mod = 1000000000;

int main()
{
    scanf("%d %d", &n, &m);

    for (int r = 1; r <= n; r++)
    {
        for (int c = 1; c <= m; c++)
        {
            char t;
            scanf(" %c", &t);
            if (t == '.')
            {
                mp[r][c] = tot++;
            }
        }
    }

    for (int r = 1; r <= n; r++)
    {
        for (int c = 1; c <= m; c++)
        {
            if (mp[r][c] == 0)
                continue;
            for (int p = 0; p < 4; p++)
            {
                int dr = r + pos[p][0];
                int dc = c + pos[p][1];

                if (mp[dr][dc] == 0)
                    continue;

                laplace[mp[r][c]][mp[r][c]]++;
                laplace[mp[r][c]][mp[dr][dc]] = laplace[mp[dr][dc]][mp[r][c]] = -1;
            }
        }
    }

    // 化上三角矩阵
    tot--;
    ll sgn = 1;
    for (int r = 1; r < tot; r++)
    {
        int d = r;
        for (int k = r; k < tot; k++)
        {
            if (laplace[k][r] != 0)
            {
                d = k;
                break;
            }
        }

        swap(laplace[d], laplace[r]);

        if (laplace[r][r] == 0)
        {
            printf("0");
            return 0;
        }

        for (int k = r + 1; k < tot; k++)
        {
            while (laplace[k][r] != 0)
            {
                swap(laplace[r], laplace[k]);
                sgn *= -1;
                ll di = laplace[k][r] / laplace[r][r];
                for (int j = r; j < tot; j++)
                {
                    laplace[k][j] = ((laplace[k][j] - laplace[r][j] * di) % mod + mod) % mod;
                }
            }
        }
    }

    ll ans = 1;

    for (int i = 1; i < tot; i++)
    {
        ans = ((ans * laplace[i][i]) % mod + mod) % mod;
    }

    ans = (ans * sgn + mod) % mod;

    printf("%lld", ans);

    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值