bzoj 4031 小Z的房间 Posted on 2019-10-18 | Views: 矩阵树定理. 把每个房间看成一个点,每堵可以打的墙看成一条边,不难发现就是要求这张无向图的生成树个数. 使用矩阵树定理计算,但模数不是质数,需要用辗转相除法来消元. 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495//%std#include<bits/stdc++.h>using namespace std;typedef long long ll;inline int read(){ int out=0,fh=1; char jp=getchar(); while ((jp>'9'||jp<'0')&&jp!='-') jp=getchar(); if (jp=='-') fh=-1,jp=getchar(); while (jp>='0'&&jp<='9') out=out*10+jp-'0',jp=getchar(); return out*fh;}const int P=1e9;int add(int a,int b){ return (a+b>=P)?(a+b-P):(a+b);}void inc(int &a,int b){ a=add(a,b);}int mul(int a,int b){ return 1LL * a * b % P;}const int MAXN=100;int n=0,a[MAXN][MAXN],id[MAXN][MAXN];void addrow(int x,int y,int k){ for(int i=1;i<=n;++i) inc(a[x][i],mul(a[y][i],k));}int ans=1;void Euclid(int x,int y)//最后使x不为0,y为0 { if(!a[y][x]) return; else if(!a[x][x]) { swap(a[x],a[y]); ans=add(P,-ans); return; } if(a[x][x]>a[y][x]) addrow(x,y,P-a[x][x]/a[y][x]); else addrow(y,x,P-a[y][x]/a[x][x]); Euclid(x,y);}void Det(){ for(int i=1;i<=n;++i) { for(int j=i+1;j<=n;++j) Euclid(i,j); ans=mul(ans,a[i][i]); if(!ans) return; } }int N,M;char buf[MAXN];int main(){ N=read(),M=read(); for(int i=1;i<=N;++i) { scanf("%s",buf+1); for(int j=1;j<=M;++j) if(buf[j]=='.') { id[i][j]=++n; if(id[i-1][j]) { int x=id[i-1][j],y=id[i][j]; a[x][y]=a[y][x]=P-1; ++a[x][x],++a[y][y]; } if(id[i][j-1]) { int x=id[i][j-1],y=id[i][j]; a[x][y]=a[y][x]=P-1; ++a[x][x],++a[y][y]; } } } --n; Det(); cout<<ans<<endl; return 0;}