1. 程式人生 > 其它 >題解 P4111 [HEOI2015]小 Z 的房間

題解 P4111 [HEOI2015]小 Z 的房間

題解

題目大意:給定一個無向圖,求它的生成樹個數。

一道裸的矩陣樹定理,外加一些建圖的技巧。

矩陣樹定理

對於一個 \(Laplace\) 矩陣,其去掉任意一行後的行列式即為答案。

行列式不會的看這裡

\(Laplace\) 矩陣是一個無向圖的鄰接矩陣轉化而來的,其中 \(L_{i,i}\) 代表 \(i\) 的度數,\(L_{i,j}\) 代表 \(i->j\) 有多少條路徑(準確的說是這些路徑的權值和,求生成樹時設為 \(1\) )。

至於怎麼算,看這裡

那麼有了這些知識,我們就可以 \(A\) 了這題。

注意,這題中模數不是質數,不能求逆元,所以我們消元時要用輾轉相除法,所以真正的時間複雜度為 \(\mathcal O((nmlog_{nm})^3)\)

Code

\(AC \kern 0.5emCODE:\)

#include<bits/stdc++.h>
#define ri register signed
#define p(i) ++i
using namespace std;
namespace IO{
    char buf[1<<21],*p1=buf,*p2=buf;
    #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
    inline int read() {
        ri x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        return x*f; 
    }
}
using IO::read;
namespace nanfeng{
    #define int long long
    #define cmax(x,y) ((x)>(y)?(x):(y))
    #define cmin(x,y) ((x)>(y)?(y):(x))
    #define FI FILE *IN
    #define FO FILE *OUT
    static const int MOD=1e9,N=15;
    int id[N][N],G[N*N][N*N],deg[N*N],n,m,cnt,ans=1;
    char h[N][N];
    inline void add(int u,int v) {G[u][v]=G[v][u]=1;}
    inline int main() {
        // FI=freopen("nanfeng.in","r",stdin);
        // FO=freopen("nanfeng.out","w",stdout);
        n=read(),m=read();
        for (ri i(1);i<=n;p(i)) {
            scanf("%s",h[i]+1);
            for (ri j(1);j<=m;p(j)) if (h[i][j]=='.') id[i][j]=p(cnt); 
        }
        for (ri i(1);i<=n;p(i)) {
            for (ri j(1);j<=m;p(j)) {
                if (id[i][j]&&id[i][j-1]) add(id[i][j],id[i][j-1]);
                if (id[i][j]&&id[i-1][j]) add(id[i][j],id[i-1][j]);
            }
        }
        for (ri i(1);i<=cnt;p(i)) {
            for (ri j(1);j<=cnt;p(j)) if (G[i][j]) p(deg[i]);
        }
        for (ri i(1);i<=cnt;p(i)) {
            for (ri j(1);j<=cnt;p(j)) {
                if (i==j) G[i][j]=deg[i];
                else G[i][j]=-G[i][j];
            }   
        }
        cnt-=1;
        for (ri i(1);i<=cnt;p(i)) {//這是高斯消元,要化成三角矩陣,而不要以高斯約旦法消成對角線矩陣。
            for (ri j(i+1);j<=cnt;p(j)) {
                while(G[j][i]) {
                    int k=G[i][i]/G[j][i];
                    for (ri l(i);l<=cnt;p(l)) G[i][l]=(G[i][l]-G[j][l]*k%MOD+MOD)%MOD;
                    swap(G[i],G[j]);
                    ans*=-1;
                }
            }
            ans=(ans*G[i][i]%MOD+MOD)%MOD;
        } //求行列式
        printf("%lld\n",(ans+MOD)%MOD);
        return 0;
    }
    #undef int
}
int main() {return nanfeng::main();}