1. 程式人生 > >【bzoj4031】[HEOI2015]小Z的房間 解題報告

【bzoj4031】[HEOI2015]小Z的房間 解題報告

【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