• 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
    ",ans);
    }
    
  • 相关阅读:
    gitlab 建仓的流程
    gitlab安装
    以普通用户登录 su root 用vncviewer:xxxxx 会报错!!exit 回到最初环境变的用户 问题解决!!!!
    linux下绘图工具dia
    rds材资收集
    export 解决环境变量的问题!!!!
    查看nginx版本号
    grep -C n "匹配字符串" 匹配字符串上下N行
    查看TOMCAT的版本
    导出数据库的结构不含数据
  • 原文地址:https://www.cnblogs.com/owenyu/p/6724730.html
Copyright © 2020-2023  润新知