• [bzoj4031][HEOI2015]小Z的房间【矩阵树定理】【高斯消元】


    【题目描述】

    Description

    你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。

    你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。

    Input

    第一行两个数分别表示n和m。

    接下来n行,每行m个字符,每个字符都会是’.’或者’*’,其中’.’代表房间,’*’代表柱子。

    Output

     一行一个整数,表示合法的方案数 Mod 10^9

    Sample Input

    3 3
    ...
    ...
    .*.

    Sample Output

    15

    HINT

    对于前100%的数据,n,m<=9

    Source

    【题解】

     矩阵树定理模板题。

    基尔霍夫矩阵C=度数矩阵-邻接矩阵

    基尔霍夫矩阵的任何一个余子式(去掉第i行第i列)的行列式的值为以i为根的生成树的数量。 --我不会证明

    行列式有几个性质。

     1.任意两行(列)互换值取相反数。 证明:逆序对变化了1或1+2*k(k为中间的行数)

     2.若存在两行(列)相同或成比例,行列式值为0。 证明:第一行取i,第二行取j与第一行取j,第二行取i互相抵消。

     3.一行加上另一行或加上的数与另一行比例相同,行列式值不变。 证明:把行列式拆开分别求和。

     4.若行列式为上三角矩阵,行列式的值为对角线元素相乘。 证明:其他项至少有一个值为0

    因此,求解行列式时,可以先高斯消元成上三角矩阵再求解。

    此外,这一题的P不是质数,所以高斯消元时要用辗转相除。

    /* --------------
        user Vanisher
        problem bzoj-4031 
    ----------------*/
    # include <bits/stdc++.h>
    # define 	ll 		long long
    # define 	N 		110
    # define 	P 		1000000000
    using namespace std;
    const ll dx[4]={1,0,-1,0}, dy[4]={0,1,0,-1};
    ll A[N][N],ans,n,m,p[N][N],place;
    char mp[N][N];
    ll read(){
    	ll tmp=0, fh=1; char ch=getchar();
    	while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
    	while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
    	return tmp*fh;
    }
    void build(ll u, ll v){
    	A[u][u]++; A[v][v]++; 
    	A[u][v]--; A[v][u]--;
    }
    void guass(ll num){
    	for (ll i=1; i<=num; i++)
    		for (ll j=1; j<=num; j++)
    			A[i][j]=(A[i][j]+P)%P;
    	for (ll i=1; i<=num; i++){
    		for (ll j=i+1; j<=num; j++){
    			while (A[j][i]!=0){
    				ll t=A[i][i]/A[j][i];
    				for (ll k=i; k<=num; k++) A[i][k]=((A[i][k]-t*A[j][k])%P+P)%P;
    				for (ll k=i; k<=num; k++) swap(A[i][k],A[j][k]);
    				ans=ans*(-1);
    			}
    		}
    	}
    } 
    ll det(ll num){
    	ans=1;
    	guass(num-1);
    	ans=(ans+P)%P;
    	for (ll i=1; i<num; i++)
    		ans=ans*A[i][i]%P;
    	return ans;
    }
    int main(){
    	n=read(), m=read();
    	for (ll i=1; i<=n; i++)
    		scanf("
    %s",mp[i]+1);
    	for (ll i=1; i<=n; i++)
    		for (ll j=1; j<=m; j++)
    			if (mp[i][j]=='.') p[i][j]=++place;
    	for (ll i=1; i<=n; i++)
    		for (ll j=1; j<=m; j++)
    			for (ll k=0; k<2; k++){
    				ll tx=i+dx[k], ty=j+dy[k];
    				if (tx>0&&ty>0&&tx<=n&&ty<=m)
    					if (mp[i][j]=='.'&&mp[tx][ty]=='.')
    						build(p[i][j],p[tx][ty]);
    			}
    	printf("%lld
    ",det(place));
    	return 0;
    }
    


  • 相关阅读:
    java单例类
    java构造方法-this关键字的用法
    java封装
    Bean的装配方式
    scope的范围
    Bean实例化(三种方法)
    依赖注入
    建立Spring项目的基础
    Androids中数据库的使用SQLite
    SharedPreferences
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136029.html
Copyright © 2020-2023  润新知