bzoj4031-小Z的房间

题目

给一个\(n\*m\)的矩阵,每个点可能为“.”或“*”,有多少种方法把矩阵中的点全部连接起来,并且每两个点之间只有一条路径。

分析

题目所求的是一个矩阵内的生成树计数。很容易把这个矩阵转化为一个图。现在我们要在这个图上求生成树计数。
这里要用到Matrix-Tree定理。
这个定理的证明十分复杂,但是描述很简单。
假设有\(n\)个点,我们的矩阵\(A\)的定义为 :

  • 如果两个点\(i\)\(j\)有直接连边,那么\(A_{ij}\)为1,否则为0
  • \(A_{ii}\)为点\(i\)的度数
    这个矩阵的任意一个\(n-1\)阶主子式(即去掉任意的第\(i\)行和第\(i\)列)的行列式就是生成树的方案数。
    在实现时,我们选择最后一行和最后一列去掉,计算剩下的行列式。当然去掉第2行和第2列,第3行和第3列,答案也是一样的。
    我们称矩阵\(A\)为kirchhoff矩阵。
    这个矩阵有几个特殊性质,也符合计数:
  • kirchhoff矩阵的行列式为0,因为每行的和都为0
  • 若图不连通,kirchhoff矩阵的任意n-1阶主子式的行列式为0
  • 若图为一棵树,kirchhoff矩阵的任意n-1阶主子式的行列式为1

时间复杂度为:\(O(n^3*logn)\)

代码

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long giant;
const int maxn=10;
const giant q=1e9;
const int maxm=maxn*maxn;
const int xx[]={-1,0,1,0};
const int yy[]={0,1,0,-1};
char s[maxn][maxn];
giant a[maxm][maxm];
void sw(giant a[],giant b[],int n) {
	for (int i=1;i<=n;++i) swap(a[i],b[i]);
}
int el(giant a[],giant b[],int t,int n) {
	int tf=1;
	if (a[t]>b[t]) sw(a,b,n),tf*=-1;
	while (a[t] && b[t]) {
		giant k=b[t]/a[t];
		for (int i=1;i<=n;++i) (b[i]=b[i]-(a[i]*k)%q+q)%=q;
		if (a[t]>b[t]) sw(a,b,n),tf*=-1;
	}
	if (!a[t]) sw(a,b,n),tf*=-1;
	return tf;
}
giant eliminate(int n) {
	int f=1;
	for (int i=1;i<n;++i) {
		if (a[i][i]==0) {
			for (int j=i+1;j<=n;++j) if (a[j][i]) {
				f*=-1;
				sw(a[i],a[j],n);
				break;
			}	
		}
		for (int j=i+1;j<=n;++j) f*=el(a[i],a[j],i,n);
	}
	giant ret=1;
	for (int i=1;i<=n;++i) ret=(ret*a[i][i]+q)%q;
	return (ret*f+q)%q;
}
int bh[maxn][maxn];
int main() {
	#ifndef ONLINE_JUDGE
		freopen("test.in","r",stdin);
	#endif
	int n,m;
	scanf("%d%d",&n,&m);
	int dx=0;
	for (int i=1;i<=n;++i) scanf("%s",s[i]+1);
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='.') bh[i][j]=++dx;
	for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) if (s[i][j]=='.') {
		int idn=bh[i][j];
		for (int k=0;k<4;++k) {
			int x=i+xx[k],y=j+yy[k];	
			if (x<1 || y<1 || x>n || y>m || s[x][y]!='.') continue;
			int id=bh[x][y];
			a[idn][id]=-1;
			a[idn][idn]++;
		}
	}
	giant ans=eliminate(dx-1);
	printf("%lld\n",ans);
}
posted @ 2017-04-17 20:39  permui  阅读(280)  评论(0编辑  收藏  举报