1. 程式人生 > >【模板】矩陣樹定理

【模板】矩陣樹定理

對了有一道假題目

解析:

先明確幾個定義。

對於一個圖GG,定義其度數矩陣為D[G]D[G],為一個nnn*n的矩陣,滿足當i!=ji!=j的時候,di,j=0d_{i,j}=0,而大對角線上的元素di,id_{i,i}值為節點viv_i的度數。

對於一個圖GG,定義其鄰接矩陣為A[G]A[G],為一個nnn*n的矩陣,滿足元素ai,ja_{i,j}等於vi,vjv_i,v_j之間直接連線的邊數,也稱為接數。

對於一個圖GG,定義其KirchoffKirchoff矩陣(C[G]C[G]

)如下,為一個nnn*n的矩陣,滿足對於i,j\forall i,j ci,j=di,jai,jc_{i,j}=d_{i,j}-a_{i,j}

KirchoffMatrixTreeKirchoff\text{ }Matrix-Tree定理內容描述如下。

對於一個圖GG,其生成樹個數等於det(C[G])det(C[G])。其中detdet表示該矩陣行列式的值。

證明是個很妙妙的東西。 然而太長了不想寫

那就主要講一講怎麼O(n3)O(n^3)求一個矩陣的行列式吧。

首先行列式的定義要補充一下。

一個nnn*n方陣AA的行列式記為det(A)det(A)A|A|

把一個元素ai,ja_{i,j}所在行列劃去後(不是置為0,而是整行整列消去),剩餘的矩陣的行列式稱為元素ai,ja_{i,j}的餘子式,記作Mi,jM_{i,j},而Ai,j=(1)j+iMi,jA_{i,j}=(-1)^{j+i}M_{i,j}稱作ai,ja_{i,j}的代數餘子式,而行列式的值定義如下det(A)=ai,j(1)i+jdet(Ai,j)det(A)=\sum a_{i,j}(-1)^{i+j}det(A_{i,j})

好的上面這個完全不說人話的表述方式你看懂了嗎?

接下來是一個稍微說人話一點的表述方式。

以一個444*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;
}