[SDOI2014]重建

Description

一个 \(n\) 个点的图 \(G\),每两个点之间都有一个概率 \(p_{i,j}\) 存在一条边。求存在生成树的概率。

Solution

对于一个特定的生成树 \(T\),其边集为 \(E\),那么生成它的概率就是

\[\prod_{e\in E} p_e \prod_{e'\notin E} (1-p_{e'}) \]

即出现树边的概率积乘上不出现非树边的概率积。那么所有生成树的概率就是

\[\sum_{T} \prod_{e\in E} p_e \prod_{e'\notin E} (1-p_{e'}) \]

但是这并不能直接套 Matrix-tree 。考虑到 Matrix-tree 求的是所有生成树的边权积之和,所以需要想办法将后面的式子化成边权积。不难发现,如果把边权设成 \(\frac{p_e}{1-p_e}\) 就能很好的解决这个问题。答案即为

\[\prod_{e} (1-p_e)\sum_{T} \prod_{e\in E} \frac{p_e}{1-p_e} \]

还有一个问题,对于一条边,其出现的概率可能是 1。这就直接导致了分母为 0,除法会出问题。一开始想的是有没有什么做法可以求给定确定边的生成树个数,然后后面放弃了……转而回来想先前的做法。主要问题在于不能除 0,然后我非常灵性地对所有数加了一个 \(eps=10^{-10}\) ……竟一发过了。

复杂度 \(O(n^3)\)

#include<stdio.h>
#define N 53
#define eps 1e-10

int n;
double ans[N][N];
int main(){
    scanf("%d",&n);
    double ans=1;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++){
       	double x;
       	scanf("%lf",&x);
       	x+=eps;
       	if(i!=j) a[i][j]=-x/(1-x),a[i][i]+=x/(1-x);
        if(i<j) ans*=(1-x);
    }
    for(int i=2;i<=n;i++){
    	for(int j=i+1;j<=n;j++){
            double t=a[j][i]/a[i][i];
            for(int k=i;k<=n;k++)
            	a[j][k]-=a[i][k]*t;
        }
    	ans*=a[i][i];
    }
    printf("%.4lf",ans);
}
posted @ 2021-01-30 20:27  Kreap  阅读(72)  评论(2编辑  收藏  举报