矩阵树定理

bzoj4031 小z的房间

题目大意:给定n*m的网格,有一些障碍点,求生成树的个数。

思路:矩阵树定理直接求解就可以了。但是因为要取模所以要稍微修改一下高斯消元,行列式交换行或列的时候行列式的值要变号。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define p 1000000000
#define LL long long
#define maxm 105
using namespace std;
char map[maxm][maxm];
int dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},bi[maxm][maxm]={0};
LL ai[maxm][maxm]={0};
char in(){
    char ch;
    while(scanf("%c",&ch)==1){
        if (ch=='.'||ch=='*') return ch;
    }
}
int work(int n){
    int i,j;LL x,y,k,ans=1,f=1,t;
    for (i=1;i<=n;++i)
      for (j=1;j<=n;++j) ai[i][j]=(ai[i][j]+p)%p;
    for (i=1;i<=n;++i){
        for (j=i+1;j<=n;++j){
            x=ai[i][i];y=ai[j][i];
            while(y!=0){
                k=x/y;x%=y;swap(x,y);f=-f;
                for (t=i;t<=n;++t) 
                  ai[i][t]=(ai[i][t]-k*ai[j][t]%p+p)%p;
                for (t=i;t<=n;++t) swap(ai[i][t],ai[j][t]);
            }
        }if (!ai[i][i]) return 0;
        ans=ans*ai[i][i]%p;
    }return (int)(ans*f%p+p)%p;
}
int main(){
    int n,m,i,j,k,x,y,tot=0,u,v;scanf("%d%d",&n,&m);
    for (i=1;i<=n;++i){
        for (j=1;j<=m;++j){map[i][j]=in();if (map[i][j]=='.') bi[i][j]=++tot;}
    }for (i=1;i<=n;++i)
      for (j=1;j<=m;++j){
          if (map[i][j]!='.') continue;
          for (k=0;k<4;++k){
              x=i+dx[k];y=j+dy[k];
              if (x<1||x>n||y<1||y>m||map[x][y]!='.') continue;
              u=bi[i][j];v=bi[x][y];
              ++ai[u][u];--ai[u][v];
          }
      }printf("%d\n",work(tot-1));
}
View Code

 

bzoj3534 重建(!!!

题目大意:给定n个城市和他们之间边通行概率,求通行道路恰是生成树的概率。

思路:设矩阵树的数组是G,G[i][j]=Pe/(1-Pe),G[i][i]=-sigma G[i][j],ci=∏(1-Pe),所以G的n-1阶主子式*tmp就是答案了。

其实原来矩阵树定理中邻接矩阵的1的意义可以看做是概率,这里的是Pe,但并不能简单的改为Pe,而是Pe/(1-Pe),因为可以发现这样G的主子式是sigma(T)∏(e∈T)Pe/(1-Pe),乘上ci之后,不是树边的相当于乘了(1-Pe),是树边的相当于乘了Pe正好是我们想要(一棵生成树的概率就是树边概率*(1-非树边概率))的答案。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 55
#define eps 1e-9
#define LD double
using namespace std;
LD ai[N][N]={0.},ci;int n;
int cmp(LD x,LD y){
    if (x-y>eps) return 1;
    if (y-x>eps) return -1;
    return 0;}
LD gauss(){
    int i,j,k;LD x;--n;
    for (i=1;i<=n;++i){
        if (cmp(ai[i][i],0.)==0)
            for (j=i+1;j<=n;++j)
                if (cmp(ai[j][i],0.)!=0){
                    for (k=1;k<=n;++k) swap(ai[i][k],ai[j][k]);
                    break;}
        for (j=i+1;j<=n;++j){
            if (cmp(ai[j][i],0.)==0) continue;
            x=ai[j][i]/ai[i][i];
            for (k=i;k<=n;++k) ai[j][k]-=ai[i][k]*x;
        }
    }for (x=1.,i=1;i<=n;++i) x*=ai[i][i];
    return fabs(x);}
int main(){
    int i,j;scanf("%d",&n);
    for (ci=1.,i=1;i<=n;++i)
      for (j=1;j<=n;++j){
          scanf("%lf",&ai[i][j]);
          if (i==j) continue;
          if (ai[i][j]>1.-eps) ai[i][j]-=eps;
          if (i<j) ci*=1.-ai[i][j];
          ai[i][j]/=1.-ai[i][j];
      }for (i=1;i<=n;++i)
        for (j=1;j<=n;++j)
          if (i!=j) ai[i][i]-=ai[i][j];
    printf("%.10f\n",gauss()*ci);
}
View Code

 

posted @ 2015-10-05 19:42  Rivendell  阅读(563)  评论(0编辑  收藏  举报