矩阵树定理

本文介绍了矩阵树定理的基本概念及应用,详细讲解了如何利用矩阵树定理解决生成树计数问题,并提供了实例和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用途

矩阵树一般用于生成树计数的问题,比如求一个无向图中生成树的个数。用矩阵树定理能极大地降低时间复杂度。

前置知识:行列式

此部分可粗略浏览,了解即可。

对于一个一阶行列式,可写作
det(a1,1)=a1,1det \left( \begin{matrix} a_{1,1} \end{matrix} \right)=a_{1,1}det(a1,1)=a1,1

对于一个二阶行列式,可写作
det(a1,1a1,2a2,1a2,2)=a1,1a2,2−a1,2a2,1det \left( \begin{matrix} a_{1,1} & a_{1,2}\\ a_{2,1} & a_{2,2} \end{matrix} \right)=a_{1,1}a_{2,2}-a{1,2}a_{2,1}det(a1,1a2,1a1,2a2,2)=a1,1a2,2a1,2a2,1

对于一个三阶的行列式,可写作
det(a1,1a1,2a1,3a2,1a2,2a2,3a3,1a3,2a3,3)=a1,1a2,2a3,3+a1,2a2,3a3,1+a1,3a2,1a3,2−a1,3a2,2a3,1−a1,2a2,1a3,3−a1,1a2,3a3,2det \left( \begin{matrix} a_{1,1} & a_{1,2} & a_{1,3}\\ a_{2,1} & a_{2,2} & a_{2,3}\\ a_{3,1} & a_{3,2} & a_{3,3} \end{matrix} \right)=a_{1,1}a_{2,2}a_{3,3}+a_{1,2}a_{2,3}a_{3,1}+a_{1,3}a_{2,1}a_{3,2}\\ \qquad\qquad \qquad \qquad\qquad\qquad-a_{1,3}a_{2,2}a_{3,1}-a_{1,2}a_{2,1}a_{3,3}-a_{1,1}a_{2,3}a_{3,2}deta1,1a2,1a3,1a1,2a2,2a3,2a1,3a2,3a3,3=a1,1a2,2a3,3+a1,2a2,3a3,1+a1,3a2,1a3,2a1,3a2,2a3,1a1,2a2,1a3,3a1,1a2,3a3,2

通过观察,我们得到nnn阶行列式的一般定义。

p1,p2,…,pnp_1,p_2,\dots,p_np1,p2,,pn表示一个111nnn的排列。若一个排列的逆序对数个数为偶数,则称这个排列为偶排列;若一个排列的逆序对数个数为奇数,则称这个排列为奇排列。


t(p1,p2,…,pn)={0,当p1,p2,…,pn是偶排列时1,当p1,p2,…,pn是奇排列时 t(p_1,p_2,\dots,p_n)= \begin{cases} 0,&当p_1,p_2,\dots,p_n是偶排列时\\ 1,&当p_1,p_2,\dots,p_n是奇排列时 \end{cases} t(p1,p2,,pn)={0,1,p1,p2,,pn是偶排列时p1,p2,,pn是奇排列时

nnn阶行列式可以写成
det(a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n)=∑p1,p2,…pn(−1)t(p1,p2,…,pn)a1,p1a2,p2⋯an,pndet \left( \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right) =\sum\limits_{p_1,p_2,\dots p_n}(-1)^{t(p_1,p_2,\dots,p_n)}a_{1,p_1}a_{2,p_2}\cdots a_{n,p_n}deta1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n=p1,p2,pn(1)t(p1,p2,,pn)a1,p1a2,p2an,pn

行列式也可写作
detA=det(a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n)=∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n∣=∣A∣det A= det \left( \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right)= \left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= |A|detA=deta1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n=a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n=A

行列式的性质

性质1

AAAnnn阶矩阵,则detAT=detAdetA^T=detAdetAT=detA

性质2

行列式任意两行(或两列)互换,行列式变号。

推论: 行列式某两行(或两列)相同时,行列式值为000

性质3

将一行(或一列)的每个数乘实数kkk,得到的行列式等于原来的行列式乘kkk

推论: 行列式有两行(或两列)成比例,则行列式值为000。特别地,当行列式有一行(或一列)全为000时,行列式值为000

性质4

∣a1,1+b1,1a1,2⋯a1,na2,1+b2,1a2,2⋯a2,n⋮⋮⋱⋮an,1+bn,1an,2⋯an,n∣=∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n∣+∣b1,1a1,2⋯a1,nb2,1a2,2⋯a2,n⋮⋮⋱⋮bn,1an,2⋯an,n∣ \left| \begin{matrix} a_{1,1}+b_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1}+b_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1}+b_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= \left| \begin{matrix} a_{1,1}& a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|+ \left| \begin{matrix} b_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ b_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ b_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|a1,1+b1,1a2,1+b2,1an,1+bn,1a1,2a2,2an,2a1,na2,nan,n=a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n+b1,1b2,1bn,1a1,2a2,2an,2a1,na2,nan,n

对列也相同。

推论: 将行列式任意一行(或一列)乘一个实数kkk再加到另一行(或另一列)上,行列式的值不变。

∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n∣=∣a1,1+k⋅aj,1a1,2+k⋅aj,2⋯a1,n+k⋅aj,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n∣\left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|= \left| \begin{matrix} a_{1,1}+k\cdot a_{j,1} & a_{1,2}+k\cdot a_{j,2} & \cdots & a_{1,n}+k\cdot a_{j,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n=a1,1+kaj,1a2,1an,1a1,2+kaj,2a2,2an,2a1,n+kaj,na2,nan,n

行列式的计算方法

对于一个行列式
detA=∣a1,1a1,2⋯a1,na2,1a2,2⋯a2,n⋮⋮⋱⋮an,1an,2⋯an,n∣detA= \left| \begin{matrix} a_{1,1} & a_{1,2} & \cdots & a_{1,n}\\ a_{2,1} & a_{2,2} & \cdots & a_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ a_{n,1} & a_{n,2} & \cdots & a_{n,n} \end{matrix} \right|detA=a1,1a2,1an,1a1,2a2,2an,2a1,na2,nan,n
通过上述性质可将其变为上三角形式
detB=∣b1,1b1,2⋯b1,n0b2,2⋯b2,n⋮⋮⋱⋮00⋯bn,n∣detB= \left| \begin{matrix} b_{1,1} & b_{1,2} & \cdots & b_{1,n}\\ 0 & b_{2,2} & \cdots & b_{2,n}\\ \vdots & \vdots & \ddots & \vdots\\ 0 & 0 & \cdots & b_{n,n} \end{matrix} \right|detB=b1,100b1,2b2,20b1,nb2,nbn,n

设变化过程中交换行(或列)的次数为sss,则detA=detB=(−1)s∏i=1nbi,idetA=detB=(-1)^s\prod\limits_{i=1}^nb_{i,i}detA=detB=(1)si=1nbi,i

也就是说,我们可以通过类似高斯消元的方式,用O(n3)O(n^3)O(n3)的时间复杂度来求一个nnn阶行列式的值。


矩阵树定理

n(1≤n≤12)n(1\leq n\leq 12)n(1n12)个点,有一些点可以连边。求有多少种连边方案,可以使这nnn个点形成一棵树。

矩阵树定理介绍

对于一个无向图GGG,有以下几个概念:

  • GGG的度数矩阵D[G]D[G]D[G]是一个n∗nn*nnn的矩阵,且满足di,j={0,i≠j点i的度数,i=jd_{i,j}= \begin{cases} 0,\qquad\qquad\quad i\neq j\\ 点i的度数,\quad i=j\quad \end{cases}di,j={0,i=ji的度数,i=j
  • GGG的邻接矩阵A[G]A[G]A[G]也是一个n∗nn*nnn的矩阵,且满足ai,j={1,点i和点j之间有边相连0,点i和点j之间没有边相连a_{i,j}= \begin{cases} 1,&点i和点j之间有边相连\\ 0,&点i和点j之间没有边相连\quad \end{cases}ai,j={1,0,i和点j之间有边相连i和点j之间没有边相连

我们定义GGGKirchhoffKirchhoffKirchhoff矩阵C[G]C[G]C[G]C[G]=D[G]−A[G]C[G]=D[G]-A[G]C[G]=D[G]A[G],则GGG所有不同生成树的个数等于其KirchhoffKirchhoffKirchhoff矩阵C[G]C[G]C[G]的任意一个n−1n-1n1阶主子式的行列式的绝对值。

n−1n-1n1阶主子式: 对于r(1≤r≤n)r(1\leq r\leq n)r(1rn),将C[G]C[G]C[G]的第rrr行和第rrr列同时去掉后得到的新矩阵,表示为Cr[G]C_r[G]Cr[G]

那么对于上面这道题,我们只需要求出其KirchhoffKirchhoffKirchhoff矩阵C[G]C[G]C[G],再求detC[G]det C[G]detC[G]的任意一个n−1n-1n1阶主子式的行列式的绝对值即可。

举例

一下是一个555个点,666条边的无向图。

在这里插入图片描述

则其KirchhoffKirchhoffKirchhoff矩阵为
C[G]=∣2−1−100−120−10−103−1−10−1−13−100−1−12∣C[G]= \left| \begin{matrix} 2 & -1 & -1 & 0 & 0\\ -1 & 2 & 0 & -1 & 0\\ -1 & 0 & 3 & -1 & -1\\ 0 & -1 & -1 & 3 & -1\\ 0 & 0 & -1 & -1 & 2\\ \end{matrix} \right| C[G]=2110012010103110113100112

r=2r=2r=2
C2[G]=∣2−100−13−1−10−13−10−1−12∣=2⋅∣3−1−1−13−1−1−12∣−(−1)⋅∣−100−13−1−1−12∣=2×8+1×(−5)=11C_2[G]= \left| \begin{matrix} 2 & -1 & 0 & 0\\ -1 & 3 & -1 & -1\\ 0 & -1 & 3 & -1\\ 0 & -1 & -1 & 2\\ \end{matrix} \right| \\ \\ =2\cdot \left| \begin{matrix} 3 & -1 & -1\\ -1 & 3 & -1\\ -1 & -1 & 2\\ \end{matrix} \right|-(-1)\cdot \left| \begin{matrix} -1 & 0 & 0\\ -1 & 3 & -1\\ -1 & -1 & 2\\ \end{matrix} \right|= 2\times 8+1\times(-5)=11 C2[G]=2100131101310112=2311131112(1)111031012=2×8+1×(5)=11

上图生成树的个数就是111111个。

我们可以用类似高斯消元的方法来求行列式的值具体操作见下面例题的代码。其时间复杂度为O(n3)O(n^3)O(n3)

证明过程比较繁琐,这里不做记录。并且在做题过程中也不需要证明,有了解,学会运用即可。


例题

SPOJ104 Highways

题目大意

给你一个有nnn个点mmm条边的无向图,求使每两个点间有且只有一条路线有多少方案(即求生成树的个数)。
1≤n≤121\leq n\leq 121n12,有多组数据。

题解

这就是一道矩阵树定理的板题。在代码中,gauss()gauss()gauss()函数其实就是类似高斯消元的函数,利用辗转相除法,用O(nlog⁡n)O(n\log n)O(nlogn)的时间复杂度来使第jjj行的第iii列的数变为000。因为还要枚举当前行iii和需要修改的行jjj,所以总时间复杂度为O(t⋅n3log⁡n)O(t\cdot n^3\log n)O(tn3logn),对于这道题来说是可以过的。

code

#include<bits/stdc++.h>
using namespace std;
int t,n,m,x,y;
long long ans,a[105][105];
void gauss(){
	for(int i=1;i<=n;i++){
		for(int j=i+1;j<=n;j++){
			while(a[j][i]){
				long long tp=a[i][i]/a[j][i];
				for(int v=i;v<=n;v++) a[i][v]-=a[j][v]*tp;
				for(int v=i;v<=n;v++) swap(a[i][v],a[j][v]);
			}
		}
	}
}
int main()
{
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(int i=0;i<=100;i++){
			for(int j=0;j<=100;j++) a[i][j]=0;
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&x,&y);
			--a[x][y];--a[y][x];
			++a[x][x];++a[y][y];
		}
		for(int i=1;i<=n;i++){
			a[i][n]=a[n][i]=0;
		}
		--n;
		gauss();
		ans=1;
		for(int i=1;i<=n;i++){
			ans=ans*a[i][i];
		}
		if(ans<0) ans=-ans;
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值