[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; }