BZOJ 3534: [Sdoi2014]重建 矩阵树定理+概率

矩阵树定理求的是 $\sum_{E} \prod_{e \in E} w(e)$  

平时我们求的大多数是方案数,所以就默认那个 $w(e)$ 是 $1$.  

而如果我们想求所有生成树权值之和的话就让那个 $w(e)$ 变成边权.   

我们在设置最开始的那个 (度数-邻接)矩阵的时候度数矩阵中 $S1_{i,i}$ 保留与 $i$ 点相连所有边之和.  

然后 $S2_{i,j}$ 是 $(i,j)$ 所有连边之和即可.   

code:

#include <cstring>
#include <cmath>  
#include <cstdio>
#include <algorithm>
#define N 303      
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;  
int n;             
double eps=1e-8;
double a[N][N],p[N][N],tot=1;                
double gauss()
{
    int i,j,k;   
    double ans=1.0;    
    for(i=2;i<=n;++i)
    {
        k=i;
        for(j=i+1;j<=n;++j)  if(fabs(a[j][i])>fabs(a[k][i])) k=j; 
        if(k!=i) swap(a[i],a[k]),ans*=-1;  
        if(fabs(a[i][i])<eps) return 0.0;  
        double inv=1.0/a[i][i];      
        for(j=i+1;j<=n;++j)
        {
            double t=a[j][i]/a[i][i];    
            for(k=i;k<=n;++k) a[j][k]-=t*a[i][k];     
        }
    }   
    for(i=2;i<=n;++i) ans*=a[i][i];   
    return ans; 
}   
int main() 
{
    // setIO("input");   
    int i,j;      
    scanf("%d",&n);  
    for(i=1;i<=n;++i) for(j=1;j<=n;++j)  scanf("%lf",&p[i][j]);                           
    for(i=1;i<=n;++i) 
        for(j=i+1;j<=n;++j) 
        {
            if(fabs(p[i][j])<eps) p[i][j]=eps;   
            if(fabs(1-p[i][j])<eps) p[i][j]=1-eps;    
            tot*=(1.0-p[i][j]);  
            a[i][i]+=p[i][j]/(1.0-p[i][j]);   
            a[j][j]+=p[i][j]/(1.0-p[i][j]);      
            a[i][j]-=p[i][j]/(1.0-p[i][j]);   
            a[j][i]-=p[i][j]/(1.0-p[i][j]);    
        }
    double ans=gauss();   
    printf("%.10f\n",ans*tot);   
    return 0;
}

  

posted @ 2020-02-03 21:29  EM-LGH  阅读(126)  评论(0编辑  收藏  举报