[bzoj4031][高斯消元][矩阵树]小Z的房间
2018-03-01 11:41
253 查看
4031: [HEOI2015]小Z的房间
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 1455 Solved: 734
[Submit][Status][Discuss]
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
矩阵树的话,反正我不会证矩阵树定理是吧,那我们理解一下行列式好了。
行列式=∑p(p是一个n的排列)(−1)p的逆序对数∗∏ia[i][pi]∑p(p是一个n的排列)(−1)p的逆序对数∗∏ia[i][pi]
推论0:
矩阵的和的行列式和矩阵的行列式的和 不相等
推论1:
如果2个矩阵只有某一行不同,那么这两个矩阵的行列式的和等于这一行相加,其他行不变的矩阵的行列式。这个用行列式的定义式分配律一下就行了。
推论2:
把行列式两行交换,行列式的值取反。这样考虑,你本来是
∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[px][px]∗a[py][py]∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[px][px]∗a[py][py]
现在是
∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[py][px]∗a[px][py]∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[py][px]∗a[px][py]
再变成a[px][py]*a[py][px],我们不去算这个式子,我们考虑把第一个式子前面的p中的px和py交换了,那么两个式子中后面的部分完全相同,而交换px,py改变的逆序对数一定是奇数个的,这个分3块随便讨论一下即可。所以就相当于是式子取反了。
推论3:
一个矩阵的两行完全相等的话,矩阵行列式为0。和推论2一模一样。把px,py处理一下,容易发现px行全部和py行相等,但是py行也是逆序对会变,所以贡献都没了。
推论4:
一个矩阵的某一行*x,行列式*x,定义式提一下公因式就行了。
推论5:
每一行的值的和是0的时候,行列式的值为0。(不会证)
推论6:
一个矩阵的某一行乘某个数加到另一行上面,显然行列式不变。
考虑这个新矩阵的行列式,由推论1容易分成2个矩阵的行列式的和的形式。
那么其中一个矩阵是一行x,另一行是yx,根据推论4,先把yx除成x,这个矩阵根据推论3行列式是0,就算再*y回去,也是0。所以提供贡献的是原矩阵。
那我们现在可以*一行加到另一行了,那就说明可以高消了是吧。注意一下就是根据推论2,你交换2行的时候需要乘-1。
那么我们怎么求解一个矩阵的行列式呢,我们先把这个矩阵消成一个上三角矩阵,然后把对角线乘起来就可以了。容易发现一个上三角矩阵的行列式的值就是其对角线的乘积(我发现不了)。
容易发现基尔霍夫矩阵是每一行的和都为0的矩阵。所以他的行列式为0。但是他的任意一个余子式的行列式都是对应图的生成树的个数。而在有向图中,就没有树的概念了,而是一个树形图。比如i是根,则Mi,i的行列式是对应图的树形图的个数。
完结撒花
我本来是不想把高消取模再拉出来写了,结果刚写矩阵树就发现了一个良心出题人,1e9的模数让我们逆元不能用了。那如果没有逆元又要取模的话要怎么办呢?我们考虑辗转相除,维护a[i][i]和a[j][i],目的是用a[i][i]把a[j][i]消掉,从辗转相除的角度考虑肯定会有一个被消成0的。那我们考虑怎么做这个过程。好像不用考虑,直接倍数做一下模一下就行了。
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 1455 Solved: 734
[Submit][Status][Discuss]
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
矩阵树的话,反正我不会证矩阵树定理是吧,那我们理解一下行列式好了。
行列式
我们设现在有一个n*n的方阵(我们的基尔霍夫矩阵一定是个方阵)行列式=∑p(p是一个n的排列)(−1)p的逆序对数∗∏ia[i][pi]∑p(p是一个n的排列)(−1)p的逆序对数∗∏ia[i][pi]
推论0:
矩阵的和的行列式和矩阵的行列式的和 不相等
推论1:
如果2个矩阵只有某一行不同,那么这两个矩阵的行列式的和等于这一行相加,其他行不变的矩阵的行列式。这个用行列式的定义式分配律一下就行了。
推论2:
把行列式两行交换,行列式的值取反。这样考虑,你本来是
∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[px][px]∗a[py][py]∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[px][px]∗a[py][py]
现在是
∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[py][px]∗a[px][py]∑p(p是一个n的排列)(−1)p的逆序对数∗(∏i!=px,i!=pya[i][pi])∗a[py][px]∗a[px][py]
再变成a[px][py]*a[py][px],我们不去算这个式子,我们考虑把第一个式子前面的p中的px和py交换了,那么两个式子中后面的部分完全相同,而交换px,py改变的逆序对数一定是奇数个的,这个分3块随便讨论一下即可。所以就相当于是式子取反了。
推论3:
一个矩阵的两行完全相等的话,矩阵行列式为0。和推论2一模一样。把px,py处理一下,容易发现px行全部和py行相等,但是py行也是逆序对会变,所以贡献都没了。
推论4:
一个矩阵的某一行*x,行列式*x,定义式提一下公因式就行了。
推论5:
每一行的值的和是0的时候,行列式的值为0。(不会证)
推论6:
一个矩阵的某一行乘某个数加到另一行上面,显然行列式不变。
考虑这个新矩阵的行列式,由推论1容易分成2个矩阵的行列式的和的形式。
那么其中一个矩阵是一行x,另一行是yx,根据推论4,先把yx除成x,这个矩阵根据推论3行列式是0,就算再*y回去,也是0。所以提供贡献的是原矩阵。
那我们现在可以*一行加到另一行了,那就说明可以高消了是吧。注意一下就是根据推论2,你交换2行的时候需要乘-1。
那么我们怎么求解一个矩阵的行列式呢,我们先把这个矩阵消成一个上三角矩阵,然后把对角线乘起来就可以了。容易发现一个上三角矩阵的行列式的值就是其对角线的乘积(我发现不了)。
矩阵树(教你怎么背诵内容)
基尔霍夫矩阵就是度数矩阵-邻接矩阵,这个在重边的时候也成立。在有向边的情况下,度数矩阵指入度。我们称Mi,j是一个矩阵的余子式,他表示的是矩阵去掉i行和j列下的行列式的值。容易发现基尔霍夫矩阵是每一行的和都为0的矩阵。所以他的行列式为0。但是他的任意一个余子式的行列式都是对应图的生成树的个数。而在有向图中,就没有树的概念了,而是一个树形图。比如i是根,则Mi,i的行列式是对应图的树形图的个数。
完结撒花
sol:
本来写高消就是为了矩阵树准备的。结果前面高消还写了挺多题的。感觉也还不错,异或方程组和同余方程组还是见见的好。我本来是不想把高消取模再拉出来写了,结果刚写矩阵树就发现了一个良心出题人,1e9的模数让我们逆元不能用了。那如果没有逆元又要取模的话要怎么办呢?我们考虑辗转相除,维护a[i][i]和a[j][i],目的是用a[i][i]把a[j][i]消掉,从辗转相除的角度考虑肯定会有一个被消成0的。那我们考虑怎么做这个过程。好像不用考虑,直接倍数做一下模一下就行了。
#include<iostream> #include<algorithm> #include<string> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; typedef long long ll; typedef double s64; int n,m; const int N=100; const int pyz=1e9; int tot,num ,c ; char sr ; const int dx[4]={0,0,1,-1}; const int dy[4]={1,-1,0,0}; int ans=1; inline void guass() { /* for(int i=1;i<=tot;++i){ for(int j=1;j<=tot;++j) printf("%d ",c[i][j]); printf("\n"); }*/ tot--; for(int i=1;i<=tot;++i) { int row; for(row=i;row<=tot;++row) if(c[row][i]) break; if(row!=i) { for(int j=1;j<=tot;++j) swap(c[row][j],c[i][j]); ans*=-1; } for(int j=i+1;j<=tot;++j) while(c[j][i]) { int t=c[j][i]/c[i][i]; for(int k=1;k<=tot;++k) c[j][k]=(c[j][k]+pyz-(ll)t*c[i][k]%pyz)%pyz; if(!c[j][i]) break; ans*=-1; for(int k=1;k<=tot;++k) swap(c[i][k],c[j][k]); } } ans=(ans+pyz)%pyz; for(int i=1;i<=tot;++i) ans=(ll)ans*c[i][i]%pyz; } int main() { // freopen("4031.in","r",stdin); // freopen(".out","w",stdout); cin>>n>>m; for(int i=1;i<=n;++i) scanf("%s",sr[i]+1); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(sr[i][j]=='.') num[i][j]=++tot; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(sr[i][j]=='.') { int x,y; for(int k=0;k<=3;++k) { x=i+dx[k]; y=j+dy[k]; if(1<=x&&x<=n&&1<=y&&y<=m&&sr[x][y]!='*') { c[num[i][j]][num[i][j]]++; c[num[i][j]][num[x][y]]--; } } } guass(); printf("%d\n",ans); }
相关文章推荐
- [BZOJ4031][HEOI2015]小Z的房间(矩阵树定理+高斯消元)
- BZOJ4031 [HEOI2015]小Z的房间 【矩阵树定理 + 高斯消元】
- [bzoj4031][HEOI2015]小Z的房间【矩阵树定理】【高斯消元】
- bzoj 4031: [HEOI2015]小Z的房间 (矩阵树定理+高斯消元)
- BZOJ 3503: [Cqoi2014]和谐矩阵( 高斯消元 )
- 挑战程序竞赛系列(43):4.1矩阵 高斯消元
- [BZOJ3503]-[CQOI2014]和谐矩阵-高斯消元
- 【CQOI2014】和谐矩阵 高斯消元
- hdu4305Lightning 生成树计数(基尔霍夫矩阵)+高斯消元+逆元
- 【线性代数公开课MIT Linear Algebra】 第二课 矩阵与高斯消元
- 【Ural1041】Nikifor【拟阵】【线性无关】【高斯消元】【矩阵的秩】
- POJ 开关问题 1830【高斯消元求矩阵的秩】
- 【BZOJ】【P2467】【中山市选2010】【生成树】【题解】【矩阵树定理+高斯消元+打表】
- 【bzoj4031】[HEOI2015]小Z的房间 矩阵树定理
- BZOJ 2467: [中山市选2010]生成树(矩阵树定理+取模高斯消元)
- 【BZOJ 3503】 [Cqoi2014]和谐矩阵|高斯消元|xor方程组
- POJ 1222 EXTENDED LIGHTS OUT(高斯消元异或矩阵)
- Luogu4783 【模板】矩阵求逆(高斯消元)
- BZOJ 3503 CQOI 2014 和谐矩阵 高斯消元
- 高斯消元求矩阵解