题目描述
你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。
你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。
输入格式
第一行两个数分别表示n和m。
接下来n行,每行m个字符,每个字符都会是’.’或者’’,其中’.’代表房间,’’代表柱子。
输出格式
一行一个整数,表示合法的方案数 Mod 10^9
输入输出样例
输入 #1复制
2 2
…
…
输出 #1复制
4
输入 #2复制
2 2
.
.
输出 #2复制
0
说明/提示
对于前20%的数据,n,m <= 3
对于前50%的数据,n,m <=5
对于前100%的数据,n,m<=9
有40%的数据保证,min(n,m)<=3
有30%的数据保证,不存在柱子
题意大概就是求生成树的总数。
我们就可以用到矩阵树定理了,建立一个行列式,直接高斯消元即可。
矩阵树定理:(度数矩阵 - 邻接矩阵)的行列式就是生成树的总数。
注意建边只能建一条,所以特判一下,让小的往大的建立即可。
AC代码:
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=110,p=1e9;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int n,m,g[N][N],a[N][N],cnt;
inline void add(int x,int y){
if(x>y) return ;
g[x][x]++; g[y][y]++; g[x][y]--; g[y][x]--;
}
int gauss(){
int res=1;
for(int i=1;i<cnt;i++){
for(int j=i+1;j<cnt;j++){
while(g[j][i]){
int t=g[i][i]/g[j][i];
for(int k=i;k<cnt;k++){
g[i][k]=(g[i][k]-t*g[j][k]+p)%p;
}
swap(g[i],g[j]);
res=-res;
}
}
res=(res*g[i][i])%p;
}
return (res+p)%p;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
char str[15]; scanf("%s",str+1);
for(int j=1;j<=m;j++) if(str[j]!='*') a[i][j]=++cnt;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<4;k++){
int tx=i+dx[k]; int ty=j+dy[k];
if(a[i][j]&&a[tx][ty]) add(a[i][j],a[tx][ty]);
}
}
}
cout<<gauss()<<endl;
return 0;
}