矩阵树定理
矩阵树定理也称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矩阵:
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=⎣⎢⎢⎡3−1−1−1−1100−102−1−10−12⎦⎥⎥⎤
那么我们从 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(⎣⎡10002−10−12⎦⎤)=3
那么这个图的生成树的数量为 3 3 3,可以证明删除任意行列的行列式的值不变。
注意,Cayley定理是矩阵树定理的一个特殊情况,因为Cayley定理说明,一个 n n n个节点的组成树的数量为 n n − 2 n^{n-2} nn−2这正好是 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=⎣⎢⎢⎢⎡n−1−1⋮−1−1n−1⋮−1⋯⋯⋱⋯−1−1⋮n−1⎦⎥⎥⎥⎤
去掉第一行第一列之后:
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(⎣⎢⎡n−1⋮−1⋯⋱⋯−1⋮n−1⎦⎥⎤)=nn−2
例题
#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;
}