[HEOI2015] 小 Z 的房间

小Z的房间

思路分析

很明显的矩阵树。

矩阵树定理如下(上面提到过):

对于一个无向图 \(G\) ,它的生成树个数等于其基尔霍夫 \(Kirchhoff\) 矩阵任何一个 \(N-1\) 阶主子式的行列式的绝对值。

因为题意没有限制方向,无向图就很显然啦。

因为要求房子联通,所以就是要将房子看作点,记得要用 \(cnt\) 重新编号。

如果两个房子相邻,就连边。注意这里可以只判断下方和右方的情况,避免重复情况,因为加边操作已经把两个点的情况都改变了。(其实只判断上方和左方也可以,看心情叭~)

然后就是高斯消元模板,这里就不赘述了。具体实现看代码吧~

有一点就是,取模时要注意负数的情况。

CODE

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int mod = 1e9;
int n,m,cnt,ans = 1,sum[105][105],num[15][15];
char s[15][15];
void add(int x,int y) {
	sum[x][x] ++, sum[y][y] ++;
	sum[x][y] --, sum[y][x] --;
}
signed main() {
	scanf("%lld %lld",&n,&m);
	for(int i = 1; i <= n; i ++) {
		scanf("%s",s[i] + 1); 
		for(int j = 1; j <= m; j ++) {
			if(s[i][j] == '.') num[i][j] = ++cnt;//点 
		}
	}
	for(int i = 1; i <= n; i ++) {//处理连边 
		for(int j = 1; j <= m; j ++) {
			if(!num[i][j]) continue;
			if(num[i + 1][j]) add(num[i][j],num[i + 1][j]);
			if(num[i][j + 1]) add(num[i][j],num[i][j + 1]);
		}
	}
	for(int i = 1; i < cnt; i ++) {
		for(int j = i + 1; j < cnt; j ++) {
			while(sum[j][i]) {
				int d = sum[i][i] / sum[j][i];
				for(int k = 1; k < cnt; k ++) sum[i][k] = (sum[i][k] - d * sum[j][k] % mod + mod) % mod,swap(sum[i][k],sum[j][k]);
				ans *= -1;
			}
		}
		ans = (ans * sum[i][i] % mod + mod) % mod;
	}
	printf("%lld",ans);
	return 0;
} 
posted @ 2021-02-05 16:37  Spring-Araki  阅读(56)  评论(0编辑  收藏  举报