正题
题目链接:https://www.luogu.com.cn/problem/P3317
题目大意
nnn个点若干条边。告诉你每条边出现的概率,求刚好出现一颗生成树的概率是多少。
解题思路
矩阵树定理是计算每个生成树的每条边乘积之和。
我们考虑将答案转换为那个形式,ai,ja_{i,j}ai,j表示i−>ji->ji−>j的边出现的概率,那么对于每棵生成树有答案
∏x−>yai,j∗∏x−/>y(1−ai,j)\prod_{x->y}a_{i,j}*\prod_{x-/>y}(1-a_{i,j})x−>y∏ai,j∗x−/>y∏(1−ai,j)
也就是
∏i=1n∏j=1n(1−ai,j)∗∏x−>yai,j1−ai,j\prod_{i=1}^n\prod_{j=1}^n(1-a_{i,j})*\prod_{x->y}\frac{a_{i,j}}{1-a_{i,j}}i=1∏nj=1∏n(1−ai,j)∗x−>y∏1−ai,jai,j
所以我们先计算出所有的1−ai,j1-a_{i,j}1−ai,j的乘积,然后再将边权变为ai,j1−ai,j\frac{a_{i,j}}{1-a_{i,j}}1−ai,jai,j用矩阵树计算即可。
时间复杂度O(n3)O(n^3)O(n3)
codecodecode
#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);
}