[SDOI2014]重建
Link
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);
}