【模板】矩阵树定理

版权声明:转载请声明出处,谢谢配合。 https://blog.csdn.net/zxyoi_dreamer/article/details/82770146

参考题目:SPOJ HIGH

对了有一道假题目


解析:

先明确几个定义。

对于一个图 G G ,定义其度数矩阵为 D [ G ] D[G] ,为一个 n n n*n 的矩阵,满足当 i ! = j i!=j 的时候, d i , j = 0 d_{i,j}=0 ,而大对角线上的元素 d i , i d_{i,i} 值为节点 v i v_i 的度数。

对于一个图 G G ,定义其邻接矩阵为 A [ G ] A[G] ,为一个 n n n*n 的矩阵,满足元素 a i , j a_{i,j} 等于 v i , v j v_i,v_j 之间直接连接的边数,也称为接数。

对于一个图 G G ,定义其 K i r c h o f f Kirchoff 矩阵( C [ G ] C[G] )如下,为一个 n n n*n 的矩阵,满足对于 i , j \forall i,j c i , j = d i , j a i , j c_{i,j}=d_{i,j}-a_{i,j}

K i r c h o f f   M a t r i x T r e e Kirchoff\text{ }Matrix-Tree 定理内容描述如下。

对于一个图 G G ,其生成树个数等于 d e t ( C [ G ] ) det(C[G]) 。其中 d e t det 表示该矩阵行列式的值。

证明是个很妙妙的东西。
然而太长了不想写

那就主要讲一讲怎么 O ( n 3 ) O(n^3) 求一个矩阵的行列式吧。

首先行列式的定义要补充一下。

一个 n n n*n 方阵 A A 的行列式记为 d e t ( A ) det(A) A |A|

把一个元素 a i , j a_{i,j} 所在行列划去后(不是置为0,而是整行整列消去),剩余的矩阵的行列式称为元素 a i , j a_{i,j} 的余子式,记作 M i , j M_{i,j} ,而 A i , j = ( 1 ) j + i M i , j A_{i,j}=(-1)^{j+i}M_{i,j} 称作 a i , j a_{i,j} 的代数余子式,而行列式的值定义如下 d e t ( A ) = a i , j ( 1 ) i + j d e t ( A i , j ) det(A)=\sum a_{i,j}(-1)^{i+j}det(A_{i,j})

好的上面这个完全不说人话的表述方式你看懂了吗?

接下来是一个稍微说人话一点的表述方式。

以一个 4 4 4*4 的方阵为例。
就是这个矩阵中 绿色的数之积+蓝色数之积+橙色数之积+红色数之积,
在这里插入图片描述

上面的结果减去这个矩阵中 绿色的数之积+蓝色数之积+橙色数之积+红色数之积,
在这里插入图片描述
上面的差的绝对值就是矩阵行列式的值。

其他大小的矩阵行列式以此类推。

于是行列式就有一些奇奇怪怪的性质。
比如一行(列),加上或减去另一行(列)每个元素的相同倍数,行列式的值不变。(证明可能什么时候才会补吧)

那么我们就可以利用高斯消元将当前矩阵消成一个上三角矩阵,那么就可以轻易求出行列式的值了。

其行列式的值就变成了对角线上的所有值之积。
证明很简单,因为这时候其他地方的积的式子中都含有至少一个元素为0,那么有贡献的就只剩下这条大对角线了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline
int getint(){
	re int num=0;
	re char c;
	while(!isdigit(c=gc()));
	while(isdigit(c))num=(num<<1)+(num<<3)+(c^48),c=gc();
	return num;
}

double a[13][13];

inline
double gauss(int n){
	for(int re i=1;i<=n;++i){
		int pos=i;
		for(int re j=i+1;j<=n;++j){
			if(fabs(a[j][i])>fabs(a[pos][i]))pos=j;
		}
		if(fabs(a[pos][i])<=1e-6)return 0;
		if(pos!=i)swap(a[pos],a[i]);
		for(int re j=i+1;j<=n;++j){
			double tmp=a[j][i]/a[i][i];
			for(int re k=1;k<=n;++k){
				a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	double ret=1;
	for(int re i=1;i<=n;++i){
		ret*=a[i][i];
	}
	return fabs(ret);
}

signed main(){
	int T=getint();
	while(T--){
		int n=getint(),m=getint();
		memset(a,0,sizeof a);
		while(m--){
			int u=getint(),v=getint();
			a[u][u]+=1,a[v][v]+=1;
			a[u][v]-=1,a[v][u]-=1;
		}
		printf("%.0lf\n",gauss(n-1));
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zxyoi_dreamer/article/details/82770146
今日推荐