zoukankan      html  css  js  c++  java
  • BZOJ4031 [HEOI2015] 小Z的房间

    @(BZOJ)[行列式, Matrix-Tree定理, 高斯消元]

    Description

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

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

    Input

    第一行两个数分别表示(n)(m)
    接下来n行,每行m个字符,每个字符都会是’.’或者’’,其中’.’代表房间,’’代表柱子。

    Output

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

    Sample Input

    3 3
    ...
    ...
    .*.
    

    Sample Output

    15
    

    HINT

    对于前(100%)的数据,(n,m le 9)

    Solution

    需要運用到Matrix-Tree定理和一些行列式的知識(見前面的<行列式學習筆記>).

    下面我们介绍一种新的方法——Matrix-Tree定理(Kirchhoff矩阵-树定理)。Matrix-Tree定理是解决生成树计数问题最有力的武器之一。它首先于1847年被Kirchhoff证明。在介绍定理之前,我们首先明确几个概念:

    1. (G)的度数矩阵(D[G])是一个(n * n)的矩阵,并且满足:当(i e j)时,(d_{i, j})=0;当(i = j)时,(d_{i, j})等于(v_i)的度数。
    2. (G)的邻接矩阵(A[G])也是一个(n * n)的矩阵, 并且满足:如果(v_i)(v_j)之间有边直接相连,则(a_{i, j}=1),否则为(0)

    我们定义(G)的Kirchhoff矩阵(也称为拉普拉斯算子)(C[G])(C[G] = D[G] - A[G]),则Matrix-Tree定理可以描述为:(G)的所有不同的生成树的个数等于其Kirchhoff矩阵(C[G])任何一个(n - 1)阶主子式的行列式的绝对值。所谓(n - 1)阶主子式,就是对于(r (1 le r le n)),将(C[G])的第(r)行、第(r)列同时去掉后得到的新矩阵,用(Cr[G])表示。

    實際上, 這個矩陣並不需要由兩個相減得來(畢竟Kirchoff矩陣一般只會考它的結論). 可以直接枚舉兩個點(u)(v), 假如(u o v)有一條連邊, 則矩陣中(det[u][u] ++)同時(det[u][v] --). 這樣得到的結果相當於這兩個矩陣相減. 然後再這個行列式上跑類似於高斯消元的東西就可以了.

    注意: 一定要把任意的第(r)行第(r)列去掉后再求出行列式的值, 否则求出的答案永远都是(0)

    還有一點要注意的:

    然后关于取模的高斯消元,有两种实现方法:
    ①求乘法逆元
    ②欧几里得算法,同时把其他项也给消掉。注意在交换的时候,根据行列式的性质,取负号。注意全部变成正数再进行gcd。

    差不多就是這些了.

    #include<cstdio>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    inline void read(int &x)
    {
    	x = 0;
    	int flag = 1;
    	char c;
    	while(! isdigit(c = getchar()))
    		if(c == '-')
    			flag *= - 1;
    	while(isdigit(c))
    		x = x * 10 + c - '0', c = getchar();
    	x *= flag;
    }
    
    inline void read(char &c)
    {
    	while(! isgraph(c = getchar()));
    }
    
    void println(int x)
    {
    	if(x < 0)
    		putchar('-'), x *= - 1;
    	if(x == 0)
    		putchar('0');
    	int ans[1 << 5], top = 0;
    	while(x)
    		ans[top ++] = x % 10, x /= 10;
    	for(; top; top --)
    		putchar(ans[top - 1] + '0');
    	putchar('
    ');
    }
    
    const int N = 1 << 4, M = 1 << 4;
    const long long MOD = (long long)1e9;
    
    const int movesLikeJagger[4][2] = {{1, 0}, {- 1, 0}, {0, 1}, {0, - 1}};
    
    int n, m;
    
    char s[N][M];
    int id[N][M];
    
    long long det[N * M][N * M];
    
    inline int legal(int x, int y)
    {
    	return x && x <= n && y && y <= m && s[x][y] == '.';
    }
    
    inline void add(int u, int v)
    {
    	det[u][u] ++, det[u][v] --;
    }
    
    long long calculate(int n)
    {
    	for(int i = 0; i < n; i ++)
    		for(int j = 0; j < n; j ++)
    			det[i][j] = (det[i][j] + MOD) % MOD;
    			
    	long long sign = 0;
    	
    	for(int i = 0; i < n; i ++)
    	{
    		int j;
    		
    		for(j = i; j < n; j ++)
    			if(det[j][i])
    				break;
    		
    		if(j == n)
    			continue;
    		
    		if(i != j)
    		{
    			sign ^= (i != j);
    			for(int k = i; k < n; k ++)
    				swap(det[j][k], det[i][k]);
    		}
    		
    		for(int j = i + 1; j < n; j ++)
    			while(det[j][i])
    			{
    				int tmp = det[j][i] / det[i][i];
    				
    				for(int k = i; k < n; k ++)
    					det[j][k] = (det[j][k] - det[i][k] * tmp % MOD + MOD) % MOD;
    					
    				if(! det[j][i])
    					break;
    					
    				sign ^= 1;
    				for(int k = i; k < n; k ++)
    					swap(det[i][k], det[j][k]);
    			}
    	}
    	
    	long long ret = 1;
    	
    	for(int i = 0; i < n; i ++)
    		ret = (ret * det[i][i]) % MOD;
    		
    	if(sign)
    		ret = (MOD - ret) % MOD;
    		
    	return ret;
    }
    
    int main()
    {
    	#ifndef ONLINE_JUDGE
    	freopen("BZOJ4031.in", "r", stdin);
    	freopen("BZOJ4031.out", "w", stdout);
    	#endif
    	
    	read(n), read(m);
    	
    	for(int i = 1; i <= n; i ++)
    		for(int j = 1; j <= n; j ++)
    			read(s[i][j]);
    			
    	int cnt = 0;
    			
    	for(int i = 1; i <= n ; i ++)
    		for(int j = 1; j <= m; j ++)
    			if(s[i][j] == '.')
    				id[i][j] = cnt ++;
    				
    	memset(det, 0, sizeof(det));
    			
    	for(int x = 1; x <= n; x ++)
    		for(int y = 1; y <= m; y ++)
    			if(legal(x, y))
    				for(int i = 0; i < 4; i ++)
    				{
    					int nextX = x + movesLikeJagger[i][0], nextY = y + movesLikeJagger[i][1];
    					if(legal(nextX, nextY))
    						add(id[x][y], id[nextX][nextY]);
    				}
    	
    	int ans = calculate(cnt - 1);
    	
    	println(ans);
    }
    
  • 相关阅读:
    虚方法(virtual)和抽象方法(abstract)的区别
    IT社区
    C#中动态加载和卸载DLL
    应用程序体系结构
    Enterprise Architect 7.0入门教程
    jQuery插件开发基础1
    asp.net页面事件执行顺序
    codesmith4.1破解版
    在Web.config配置文件中自定义配置节点
    小巧实用的节拍器软件FineMetronome介绍 原创
  • 原文地址:https://www.cnblogs.com/ZeonfaiHo/p/6421816.html
Copyright © 2011-2022 走看看