题目描述
https://www.luogu.org/problemnew/show/P3317
给出一个图每条边存在的概率,求恰好生成一棵树的概率。(1 < N < =50)
思路
这题让我对矩阵树定理有了新的更本质的认识。
地球人都知道,基尔霍夫矩阵的任意n-1阶代数余子式就是生成树数量。这代表基尔霍夫矩阵的代数余子式皆相同且与余子式绝对值相等。
其实基尔霍夫矩阵在计算的东西是生成树每条边权的乘积的和。由于缺省值为1,所以和就是个数。在这题我们在边上放概率,可以尝试求出生成一棵树的概率和。
我们要求的无非是它(
T
是生成树)
=∑T∏e∈Tpe∏(1−pe)∏e∈T(1−pe)
=∏(1−pe)∑T∏e∈Tpe1−pe
我们将右边那一坨作为新的边权,然后求高消求代数余子式即可。
对了,如果 pe=1 呢?此时 11−pe=∞ ,因为 1Eps=∞ ,直接让 pe−=Eps 即可。
代码
#include <bits/stdc++.h>
#define Eps 1e-10
#define maxn 55
using namespace std;
double G[maxn][maxn], A[maxn][maxn], Violet = 1.0;
int n;
double Det(){
int rev = 0;
double ans = 1.0;
for(int i = 2; i <= n; i++){
int x = i;
for(; x <= n && fabs(A[x][i]) < Eps; x++);
if(x != i) rev ^= 1, swap(A[x], A[i]);
if(fabs(A[i][i]) < Eps) return 0.0;
ans *= A[i][i];
for(int j = i+1; j <= n; j++){
if(fabs(A[j][i]) < Eps) continue;
double Mul = A[j][i] / A[i][i];
for(int k = 2; k <= n; k++)
A[j][k] -= A[i][k] * Mul;
}
}
if(rev) return -ans;
return ans;
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){
scanf("%lf", &G[i][j]);
if(i >= j) continue;
if(fabs(G[i][j] - 1.0) < Eps) G[i][j] -= Eps;
double P = G[i][j] / (1.0 - G[i][j]);
A[i][j] -= P;
A[j][i] -= P;
A[i][i] += P;
A[j][j] += P;
Violet *= 1.0 - G[i][j];
}
printf("%.7lf\n", Violet * Det());
return 0;
}