P3317-[SDOI2014]重建【矩阵树定理,数学期望】

正题

题目链接:https://www.luogu.com.cn/problem/P3317


题目大意

n n 个点若干条边。告诉你每条边出现的概率,求刚好出现一颗生成树的概率是多少。


解题思路

矩阵树定理是计算每个生成树的每条边乘积之和。

我们考虑将答案转换为那个形式, a i , j a_{i,j} 表示 i > j i->j 的边出现的概率,那么对于每棵生成树有答案
x > y a i , j x / > y ( 1 a i , j ) \prod_{x->y}a_{i,j}*\prod_{x-/>y}(1-a_{i,j})
也就是
i = 1 n j = 1 n ( 1 a i , j ) x > y a i , j 1 a i , j \prod_{i=1}^n\prod_{j=1}^n(1-a_{i,j})*\prod_{x->y}\frac{a_{i,j}}{1-a_{i,j}}
所以我们先计算出所有的 1 a i , j 1-a_{i,j} 的乘积,然后再将边权变为 a i , j 1 a i , j \frac{a_{i,j}}{1-a_{i,j}} 用矩阵树计算即可。

时间复杂度 O ( n 3 ) O(n^3)


c o d e code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=51;
const double eps=1e-8;
int n;
double a[N][N],ans;
void Gauss(){
	for(int i=1;i<n;i++){
		int mx=i;
		for(int j=i+1;j<n;j++)
			if(fabs(a[j][i])>fabs(a[mx][i]))
				mx=j;
		if(mx!=i)
			for(int j=1;j<n;j++)
				swap(a[i][j],a[mx][j]);
		for(int j=i+1;j<n;j++){
			double mul=a[j][i]/a[i][i];
			for(int k=i;k<n;k++)
				a[j][k]-=a[i][k]*mul;
		}
		if(fabs(a[i][i])<eps){
			ans=0;return;
		}
	}
	for(int i=1;i<n;i++)
		ans=ans*a[i][i];
	ans=fabs(ans);
}
int main()
{
	scanf("%d",&n);ans=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			scanf("%lf",&a[i][j]);
			if(a[i][j]<eps) a[i][j]=eps;
			if(1-a[i][j]<eps) a[i][j]=1-eps;
			if(i<j) ans*=1-a[i][j];
			a[i][j]=a[i][j]/(1-a[i][j]);
		}
	for(int i=1;i<=n;i++){
		a[i][i]=0;
		for(int j=1;j<=n;j++)
			if(i!=j)
				a[i][i]-=a[i][j];
	}
	Gauss();
	printf("%.10lf",ans);
}

猜你喜欢

转载自blog.csdn.net/Mr_wuyongcong/article/details/104279882