【bzoj4031】[HEOI2015]小Z的房間 解題報告
阿新 • • 發佈:2018-12-20
【bzoj4031】[HEOI2015]小Z的房間
Description
你突然有了一個大房子,房子裡面有一些房間。事實上,你的房子可以看做是一個包含\(n*m\)個格子的格狀矩形,每個格子是一個房間或者是一個柱子。在一開始的時候,相鄰的格子之間都有牆隔著。
你想要打通一些相鄰房間的牆,使得所有房間能夠互相到達。在此過程中,你不能把房子給打穿,或者打通柱子(以及柱子旁邊的牆)。同時,你不希望在房子中有小偷的時候會很難抓,所以你希望任意兩個房間之間都只有一條通路。現在,你希望統計一共有多少種可行的方案。
Input
第一行兩個數分別表示\(n\)和\(m\)。
接下來\(n\)行,每行\(m\)
.
或者*
,其中.
代表房間,*
代表柱子。
Output
一行一個整數,表示合法的方案數 \(\bmod 10^9\)
HINT
對於前\(100\%\)的資料,\(n,m\le 9\)
矩陣樹定理,發現模數不為質數,所以在高斯消元的時候輾轉相除就可以了。
注意要統計行列式正負性,因為\(x\)和\(mod-x\)沒法直接判斷
複雜度多帶一個\(\log\)
#include <cstdio> #include <algorithm> const int N=100; const int mod=1e9; char c[10][10]; int n,m,a[N][N],p[N][N],cnt,ans=1,f=1; const int dx[5]={0,0,1,0,-1}; const int dy[5]={0,-1,0,1,0}; void Gauss() { for(int i=1;i<=n;i++) { if(!a[i][i]) return; for(int j=i+1;j<=n;j++) { while(a[i][i]) { int d=a[j][i]/a[i][i]; for(int k=i;k<=n;k++) a[j][k]=(a[j][k]+mod-1ll*d*a[i][k]%mod)%mod; std::swap(a[i],a[j]); f*=-1; } std::swap(a[i],a[j]); f*=-1; } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("\n"); for(int j=1;j<=m;j++) { scanf("%c",&c[i][j]); if(c[i][j]=='.') p[i][j]=++cnt; } } for(int i=1;i<=n;i++) for(int u,j=1;j<=m;j++)//’.’代表房間,’*’代表柱子 if(u=p[i][j]) { for(int v,k=1;k<=4;k++) { int ti=i+dx[k],tj=j+dy[k]; if(v=p[ti][tj]) ++a[u][u],--a[u][v]; } } n=cnt-1; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) (a[i][j]+=mod)%=mod; Gauss(); int ans=1; for(int i=1;i<=n;i++) ans=(1ll*ans*a[i][i])%mod; ans=f==1?ans:mod-ans; printf("%d\n",ans); return 0; }
2018.12.20