基尔霍夫(kirchhoff)矩阵树定理

引入问题

给n个点m条边的图,求该图的最小生成树个数。

定理内容

我们对这个图构造两个矩阵,分别是这个图的连通矩阵和度数矩阵。连通矩阵

S

1

S1

S1的第

i

i

i行第

j

j

j列上的数字表示原无向图中编号为

i

i

i和编号为

j

j

j的两个点之间的边的条数。度数矩阵

S

2

S2

S2只有斜对角线上有数字,即只有第

i

i

i行第

i

i

i列上有数字,表示编号为i的点的度数是多少。我们将两个矩阵相减,即

S

2

S

1

S2−S1

S2−S1,我们记得到的矩阵为

T

T

T,我们将矩阵

T

T

T去掉任意一行和一列(一般情况去掉最后一行和最后一列的写法比较多)得到

T

T^′

T′,最后生成树的个数就是这个矩阵

T

T^′

T′的行列式。 ——摘自:《矩阵树定理浅谈》

基尔霍夫矩阵

定义:如果图

D

D

D有总共

N

N

N个点,那么图

D

D

D的基尔霍夫矩阵

G

G

G可以表示为:

G

i

j

=

{

d

e

g

r

e

e

(

i

)

i

=

j

c

n

t

(

i

,

j

)

i

j

G_{ij}=\left\{ \begin{array}{rcl} & degree(i) & &{i=j}\\ &−cnt(i,j) & &{i \neq j}\\ \end{array}\right.

Gij​={​degree(i)−cnt(i,j)​​i=ji​=j​

d

e

g

r

e

e

degree

degree:结点i的度数

c

n

t

cnt

cnt :两点之间的边数

解决问题的关键就是利用基尔霍夫矩阵。

以下假设图无重边,证明摘自:《生成树的计数及其应用》周冬

性质

G

=

0

\left|G\right|=0

∣G∣=0,证明:显然每行的和为0,把所有列都加到第一列得0。若图

D

D

D不连通,则其对应的基尔霍夫矩阵的任意

n

1

n-1

n−1阶主子式为0。

证明:假设图

D

D

D有

k

(

k

>

1

)

k(k>1)

k(k>1)个连通份量

D

1

,

D

2

,

.

.

.

,

D

k

D_1,D_2,...,D_k

D1​,D2​,...,Dk​。我们可以通过矩阵的变换使得这些连通分量的排列如下:(每次变换行列都交换一次,对应行列式符号不变)

(

D

1

0

0

0

D

2

0

0

0

0

D

k

)

\begin{pmatrix} D_1 & 0 &\cdots& 0\\\\ 0 & D_2 &\cdots&0\\\\ \vdots &\vdots & \ddots&\vdots\\\\ 0 & 0 & 0 &D_k\\\\ \end{pmatrix}

⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛​D1​0⋮0​0D2​⋮0​⋯⋯⋱0​00⋮Dk​​⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞​(矩阵中的0元素确保连通分量之间不连通)

G

\left|G\right|

∣G∣=

G

1

\left|G_1\right|

∣G1​∣

G

2

G

k

\left|G_2\right|\cdots\left|G_k\right|

∣G2​∣⋯∣Gk​∣,据此可得出对应基尔霍夫矩阵的任意

n

1

n-1

n−1阶主子式为0。

若图

D

D

D是树,其对应基尔霍夫矩阵

G

G

G的任意一个

n

1

n-1

n−1阶主子式为1。 证明:往证,可以不断变换

G

\left|G\right|

∣G∣得到一上三角行列式,且主对角线元素为1。 第一步:假设

v

r

v_r

vr​为根结点,以

v

r

v_r

vr​为首让每个结点根据与

v

r

v_r

vr​的距离从小到大排序,同距离的可任意排。显然对于结点

v

j

v_j

vj​,其父亲

v

i

v_i

vi​必定在前面。 第二步:将

G

\left|G\right|

∣G∣根据排好的序列调整行列元素,将行列不断交换。因为交换为偶数次,符号不变。此外继续调整,对每个结点

v

i

v_i

vi​的儿子

v

i

1

,

v

i

2

,

.

.

.

,

v

i

k

v_{i_1},v_{i_2},...,v_{i_k}

vi1​​,vi2​​,...,vik​​,儿子对应的每一列都加到第

i

i

i列上。 第三步:对于最后一列,对应结点只与父亲相连,故度为1,符合条件。用数学归纳法,目前处理的是第

i

i

i列,假设第

i

+

1

i+1

i+1到第

n

n

n列都已经符合条件,即对角线元素为1。对每个结点

v

i

v_i

vi​的儿子

v

i

1

,

v

i

2

,

.

.

.

,

v

i

k

v_{i_1},v_{i_2},...,v_{i_k}

vi1​​,vi2​​,...,vik​​,其对应列的第

i

i

i行元素为-1,第

i

j

i_j

ij​行(对角线)的元素为1,再看第

i

i

i列,对应的儿子行元素都为-1,第

i

i

i行元素为

k

+

1

k+1

k+1(儿子和父亲),故都加到第

i

i

i列后,第

i

i

i行元素变为1,下面每行都为0。因此,最终构造了上三角行列式且主对角元素为1,任意抽去第

i

i

i行第

i

i

i列,行列式值为1,证毕。

定理证明

引入定义,矩阵

B

B

B为

n

×

m

n\times m

n×m矩阵,分别对应结点和边。

B

i

j

=

{

1

v

i

e

j

1

v

i

e

j

0

o

t

h

e

r

w

i

s

e

B_{ij}=\left\{ \begin{array}{rcl} & -1 & &v_i为边e_j起点\\ &1 & &v_i为边e_j终点\\ &0 & &otherwise\\ \end{array}\right.

Bij​=⎩⎨⎧​​−110​​vi​为边ej​起点vi​为边ej​终点otherwise​

容易验证:

B

B

T

=

G

BB^T=G

BBT=G,若去掉

B

B

B中第

r

r

r行得到

B

r

B_r

Br​,有

G

r

=

B

r

B

r

T

G_r=B_rB_r^T

Gr​=Br​BrT​,即

G

G

G去掉第

r

r

r行和第

r

r

r列。

看一下Binet-Cauchy公式:

设矩阵

A

A

A为

p

×

q

p\times q

p×q矩阵,矩阵

B

B

B为

q

×

p

q\times p

q×p矩阵,有

d

e

t

(

A

B

)

=

{

0

p

>

q

d

e

t

(

A

)

d

e

t

(

B

)

p

=

q

1

j

1

<

j

2

<

<

j

p

q

A

(

1

2

p

j

1

j

2

j

p

)

B

(

j

1

j

2

j

p

1

2

p

)

p

<

q

det(AB)=\left\{ \begin{array}{rcl} &0& 当p>q时\\ &det(A)det(B)& 当p=q时\\ & \sum\limits_{1\leq j_1

det(AB)=⎩⎪⎪⎪⎪⎨⎪⎪⎪⎪⎧​​0det(A)det(B)1≤j1​q时当p=q时当p

其中

A

(

1

2

p

j

1

j

2

j

p

)

A\begin{pmatrix} 1 &2&\cdots&p\\\\ j_1&j_2&\cdots&j_p \end{pmatrix}

A⎝⎛​1j1​​2j2​​⋯⋯​pjp​​⎠⎞​表示

A

A

A的第

j

1

,

j

2

,

.

.

.

,

j

p

j_1,j_2,...,j_p

j1​,j2​,...,jp​列所成的子式,

B

(

j

1

j

2

j

p

1

2

p

)

B\begin{pmatrix} j_1&j_2&\cdots&j_p\\\\ 1&2&\cdots&p \end{pmatrix}

B⎝⎛​j1​1​j2​2​⋯⋯​jp​p​⎠⎞​表示

B

B

B的第

j

1

,

j

2

,

.

.

.

,

j

p

j_1,j_2,...,j_p

j1​,j2​,...,jp​行所成的子式。

从而得到:

d

e

t

(

G

r

)

=

d

e

t

(

B

r

B

r

T

)

=

x

{

1

,

2

,

.

.

,

m

}

x

=

n

1

d

e

t

(

B

r

x

B

r

x

T

)

=

x

{

1

,

2

,

.

.

,

m

}

x

=

n

1

(

d

e

t

(

B

r

x

)

)

2

det(G_r)=det(B_rB_r^T)=\sum\limits_{x\subset \{1,2,..,m\}\atop\left|x\right|=n-1}det(B_r^xB_r^{x^T})=\sum\limits_{x\subset \{1,2,..,m\}\atop\left|x\right|=n-1}(det(B_r^x))^2

det(Gr​)=det(Br​BrT​)=∣x∣=n−1x⊂{1,2,..,m}​∑​det(Brx​BrxT​)=∣x∣=n−1x⊂{1,2,..,m}​∑​(det(Brx​))2

解释一下:

B

r

B_r

Br​为

(

n

1

)

×

m

(n-1)\times m

(n−1)×m矩阵(

m

>

n

1

m>n-1

m>n−1),则

d

e

t

(

B

r

B

r

T

)

=

det(B_rB_r^T)=

det(Br​BrT​)=所有

n

1

n-1

n−1列构成的子式的行列式的平方和。

B

r

x

B_r^x

Brx​表示

B

r

B_r

Br​中边元素属于包含于边集

E

E

E的子集

x

x

x的所在列抽出构成的新矩阵,显然

B

r

x

B

r

x

T

B_r^xB_r^{x^T}

Brx​BrxT​可以看作一个

n

n

n个点和

n

1

n-1

n−1条边构成的图的基尔霍夫矩阵的的一个

n

1

n-1

n−1阶主子式,当

x

x

x为树时,行列式值为1,否则图不连通值为0,考察所有边集元素个数为

n

1

n-1

n−1的子集,结果就恰好为生成树个数!

对于含重边的图该定理仍然适用,但是如何证明尚且不知…

应用部分

模板题:P4111 [HEOI2015]小 Z 的房间

#include

#include

#include

using namespace std;

typedef long long ll;

const ll mod = 1e9;

const int dir[4][2] = { {1,0},{0,1}};

int n, m;

char g[10][10];

ll K[100][100];

int id[10][10];

void update(int x, int y) {

K[x][y]--; K[y][x]--;

K[x][x]++; K[y][y]++;

}

ll det(int n) {

ll res = 1;

for (int i = 1; i <= n; i++) {//依次把第i列,从第i+1行到第n行变为0

for (int j = i + 1; j <= n; j++) {

while (K[j][i]) {//直到为0

ll t = K[i][i] / K[j][i];//计算第i列,第i行是第j行的几倍

for (int k = i; k <= n; k++) {

K[i][k] = (K[i][k] - t * K[j][k] % mod + mod) % mod;

swap(K[i][k], K[j][k]);//把第i行元素变为0后与第j行交换

}

res = -res;//每次交换行 值变为原来的负数

}

}

if (!K[i][i])return 0;

res = (res * K[i][i] % mod + mod) % mod;//将行列式化为上三角行列式后 值为对角线元素的乘积

}

return res;

}

char read() {

char ch = getchar();

while (ch != '.' && ch != '*')ch = getchar();

return ch;

}

int main(void) {

memset(K, 0, sizeof(K));

memset(id, 0, sizeof(id));

scanf("%d %d", &n, &m);

int cnt = 0;

for (int i = 1; i <= n; i++) {

for (int j = 1; j <= m; j++) {

g[i][j] = read();

if (g[i][j] == '.')id[i][j] = ++cnt;

}

}

for (int i = 1; i <= n; i++)

for (int j = 1; j <= m; j++)

if (g[i][j] == '.') {

if (id[i - 1][j])update(id[i - 1][j], id[i][j]);

if (id[i][j - 1])update(id[i][j - 1], id[i][j]);

}

printf("%lld\n", det(cnt - 1) % mod);

return 0;

}