关于矩阵树定理,不关心证明的话,可以直接看这个结论:https://www.cnblogs.com/yangsongyi/p/10697176.html
这题的意图其实很明显,就是给一个图,问你有几个生成树。需要注意的是,柱子的点不能放进图内,不然就没有生成树了。注意建图。
另外,对于这种整数的、用不了逆元的高斯消元,我们可以才用辗转相除的方法,复杂度多一个log,具体实现看代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int (i)=(a);(i)<=(b);++i)
#define per(i,a,b) for(int (i)=(b);(i)>=(a);--i)
const int maxn=9*9+5;
const int mod=1e9;
char mp[maxn][maxn];
int n,m;
int dir[4][2]={1,0,-1,0,0,1,0,-1};
int id[maxn][maxn],tot;
#define eps (1e-16)
int A[maxn*maxn][maxn*maxn];
int gauss(int n){
int ans=1;
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
while(A[j][i]){
//辗转相除
int t=A[i][i]/A[j][i];
for(int k=i;k<=n;++k){
A[i][k]-=(1ll*A[j][k]*t)%mod;
A[i][k]=(1ll*A[i][k]%mod+mod)%mod;
swap(A[i][k],A[j][k]);
}
ans*=-1;
//行列式交换行
}
}
if(A[i][i]==0)return 0;
ans=(1ll*ans*A[i][i])%mod;
}
return (ans%mod+mod)%mod;
}
signed main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%s",mp[i]+1);
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;j++){
if(mp[i][j]=='.')id[i][j]=++tot;//柱子不放入图。
}
}
rep(i,1,n){
rep(j,1,m){
if(mp[i][j]!='.')continue;
for(int k=0;k<4;k++){
if(mp[i][j]=='.'&&mp[i+dir[k][0]][j+dir[k][1]]=='.'){
A[id[i][j]][id[i][j]]+=1;
A[id[i][j]][id[i+dir[k][0]][j+dir[k][1]]]-=1;
}
}
}
}
//注意下,矩阵树定理需要去掉一行或者一列,这里选择去掉最后一行和最后一列
int res=gauss(tot-1);
printf("%d\n",res);
return 0;
}