目前我也只做过一些矩阵树的模板题,对于这个神奇的算法了解并不深入,再加上这个算法的证明需要一定的线性代数的基础,所以这篇博客目前只能说是我对于这个定理自己的理解,重点并不在于证明。
矩阵树问题直观地说,就是给出一个图,求在这个图中生成树的方案数
首先将这个图转换成一个矩阵,这个矩阵每一个点 (i,j)
用-1表示是否有一条边从 i到j 相连,
如果 i=j ,这个位置就表示i点的度数
根据Matrix-Tree定理就可以得到:
这个图的生成树之和,就是这个矩阵删去任意一行和一列,在剩下的矩阵中的行列式的绝对值。(其实个人认为如果不是dalao看到这里就足够了,反正考场上也不会要你证明)
证明过程相当复杂,再加上我身为中年选手并没有太多时间和精力写这类证明(我看别人的都要看半天,现在一个小时不到就要肝出来明显不现实),因此证明只有主干部分,很多细节并未涉及。
首先我们定义一下几个矩阵
设C矩阵表示我们刚才所说的矩阵
设B矩阵表示一个邻接矩阵,即如有边 x 表示 (u,v) ,那么 Bux=−1,若为有向图,则Bvx=1,无向图Bvx=−1
不难发现
C=BBT,BT表示B矩阵行列互换后的矩阵
设Br为B矩阵删去r行后的矩阵
设BFr表示将Br中不属于边集F的边删去后的矩阵
经过大约1页纸的证明我们可以得到:
如果F中存在环,那么 dat(BFr)=0
再经过大约2页纸的证明还可以得到:(E表示边集)
接下来就是求行列式
通过行列式的性质:
1.交换任意两行,行列式变号。
2.把任意一行乘上一个常数加在另一行上,行列式结果不变
这样我们就可以用高斯消元,求出上三角形,这样一来整个矩阵对行列式有贡献的就只剩下主对角线上的值。将主对角线上的值乘起来就是我们要的行列式。
以下是模板题BZOJ4031的题解
你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。
你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。
首先对于每一个房间,向周围四个是房间的点连一条边,求最后的生成树的个数即可。(其实就是板)
#include
#include
#include
#include
#define SF scanf
#define PF printf
using namespace std;
void Read(int &x){
x=0;
char c;
bool flag=0;
while(c=getchar(),c!=EOF&&(c<'0'||c>'9')&&c!='-');
if(c=='-')
flag=1;
else
x=c-'0';
while(c=getchar(),c!=EOF&&c>='0'&&c<='9')
x=x*10+1*(c-'0');
if(flag==1)
x=-x;
}
#define MAXN 200
long long d[MAXN][MAXN],mod;
int w[8][4]={{0,-1},{-1,0},{1,0},{0,1}},num[MAXN][MAXN];
void swa(int x,int y,int n,int &flag){
for(int i=1;i<=n;i++)
swap(d[x][i],d[y][i]);
flag^=1;
}
long long guess(int n,int m){
int r=2,c=2,flag=0;
for(;c<=n&&r<=m;r++,c++){
int k=r;
while(k<=m&&d[k][c]==0)
k++;
if(k>m) return 0;
if(r!=k) swa(k,r,n,flag);
for(int i=k+1;i<=m;i++){
while(d[i][c]!=0){
long long delta=d[i][c]/d[r][c];
for(int j=c;j<=n;j++)
d[i][j]=(d[i][j]-delta*d[r][j])%mod;
if(d[i][c]==0)
break;
swa(i,r,n,flag);
}
}
}
if(r!=m+1)
return 0;
long long res=1;
for(int i=2;i<=n;i++){
res*=d[i][i];
res%=mod;
}
if(flag==1)
res=-res;
return (res+mod)%mod;
}
int n,m,k;
char s[MAXN][MAXN];
int main(){
Read(n),Read(m);
mod=1000000000;
int cnt=0;
for(int i=1;i<=n;i++){
SF("%s",s[i]);
for(int j=0;jif(s[i][j]=='.')
num[i][j]=++cnt;
}
for(int i=1;i<=n;i++)
for(int j=0;jif(s[i][j]=='.'){
for(int k=0;k<4;k++){
int x=i+w[k][0];
int y=j+w[k][1];
if(x<1||y<0||x>n||y>=m||s[x][y]=='*')
continue;
d[num[i][j]][num[x][y]]=-1;
d[num[i][j]][num[i][j]]++;
}
}
/*for(int i=1;i<=cnt;i++){
for(int j=1;j<=cnt;j++)
PF("%d ",d[i][j]);
PF("\n");
}*/
long long sum=guess(cnt,cnt);
PF("%lld\n",sum);
}