[HEOI2015] 小 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;
}