[bzoj4031][HEOI2015]小Z的房间【矩阵树定理】【高斯消元】

【题目描述】

Description

你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。

你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。

Input

第一行两个数分别表示n和m。

接下来n行,每行m个字符,每个字符都会是’.’或者’*’,其中’.’代表房间,’*’代表柱子。

Output

 一行一个整数,表示合法的方案数 Mod 10^9

Sample Input

3 3
...
...
.*.

Sample Output

15

HINT

对于前100%的数据,n,m<=9

Source

【题解】

 矩阵树定理模板题。

基尔霍夫矩阵C=度数矩阵-邻接矩阵

基尔霍夫矩阵的任何一个余子式(去掉第i行第i列)的行列式的值为以i为根的生成树的数量。 --我不会证明

行列式有几个性质。

 1.任意两行(列)互换值取相反数。 证明:逆序对变化了1或1+2*k(k为中间的行数)

 2.若存在两行(列)相同或成比例,行列式值为0。 证明:第一行取i,第二行取j与第一行取j,第二行取i互相抵消。

 3.一行加上另一行或加上的数与另一行比例相同,行列式值不变。 证明:把行列式拆开分别求和。

 4.若行列式为上三角矩阵,行列式的值为对角线元素相乘。 证明:其他项至少有一个值为0

因此,求解行列式时,可以先高斯消元成上三角矩阵再求解。

此外,这一题的P不是质数,所以高斯消元时要用辗转相除。

/* --------------
    user Vanisher
    problem bzoj-4031 
----------------*/
# include <bits/stdc++.h>
# define 	ll 		long long
# define 	N 		110
# define 	P 		1000000000
using namespace std;
const ll dx[4]={1,0,-1,0}, dy[4]={0,1,0,-1};
ll A[N][N],ans,n,m,p[N][N],place;
char mp[N][N];
ll read(){
	ll tmp=0, fh=1; char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
	return tmp*fh;
}
void build(ll u, ll v){
	A[u][u]++; A[v][v]++; 
	A[u][v]--; A[v][u]--;
}
void guass(ll num){
	for (ll i=1; i<=num; i++)
		for (ll j=1; j<=num; j++)
			A[i][j]=(A[i][j]+P)%P;
	for (ll i=1; i<=num; i++){
		for (ll j=i+1; j<=num; j++){
			while (A[j][i]!=0){
				ll t=A[i][i]/A[j][i];
				for (ll k=i; k<=num; k++) A[i][k]=((A[i][k]-t*A[j][k])%P+P)%P;
				for (ll k=i; k<=num; k++) swap(A[i][k],A[j][k]);
				ans=ans*(-1);
			}
		}
	}
} 
ll det(ll num){
	ans=1;
	guass(num-1);
	ans=(ans+P)%P;
	for (ll i=1; i<num; i++)
		ans=ans*A[i][i]%P;
	return ans;
}
int main(){
	n=read(), m=read();
	for (ll i=1; i<=n; i++)
		scanf("\n%s",mp[i]+1);
	for (ll i=1; i<=n; i++)
		for (ll j=1; j<=m; j++)
			if (mp[i][j]=='.') p[i][j]=++place;
	for (ll i=1; i<=n; i++)
		for (ll j=1; j<=m; j++)
			for (ll k=0; k<2; k++){
				ll tx=i+dx[k], ty=j+dy[k];
				if (tx>0&&ty>0&&tx<=n&&ty<=m)
					if (mp[i][j]=='.'&&mp[tx][ty]=='.')
						build(p[i][j],p[tx][ty]);
			}
	printf("%lld\n",det(place));
	return 0;
}


posted @ 2018-01-27 11:38  Vanisher  阅读(144)  评论(0编辑  收藏  举报