题意:给定一张图,每条边的边权是一个 0~1 的随机实数,求最大边最小的生成树的期望权值。
n n n 个 [ 0 , 1 ] [0,1] [0,1] 的随机变量,第 k k k 大的期望是 k / ( n + 1 ) k/(n+1) k/(n+1)。
a n s × ( m + 1 ) = ∑ k P ( 加 入 k 条 边 后 图 恰 好 连 通 ) × k ans\times (m+1)=\sum_{k}P(加入 k 条边后图恰好连通)\times k ans×(m+1)=∑kP(加入k条边后图恰好连通)×k
它等于 ∑ k = 1 m P ( 加 入 ≥ k 条 边 后 图 连 通 ) \sum_{k=1}^{m}P(加入 \geq k 条边后图连通) ∑k=1mP(加入≥k条边后图连通),又等价于 ∑ k P ( 加 入 k − 1 条 边 后 图 不 连 通 ) \sum_kP(加入k-1 条边后图不连通) ∑kP(加入k−1条边后图不连通)。
于是现在我们要求选出 i 条边,不连通的概率 → \rightarrow → 方案数(保证精度?)。
f[i][s][0/1] 表示在 s 集合里选了 i 条边,s 这个集合里的点 连通/不连通 的方案数。枚举子集转移。
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define ld long double
using namespace std;
struct edge {
int u,v;
}e[1000];
ll f[50][1<<10],g[50][1<<10],C[120][120];
int sum[1<<10],num[1<<10];
int read()
{
int x=0;char c=getchar(),flag='+';
while(!isdigit(c)) flag=c,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return flag=='-'?-x:x;
}
int main()
{
int n=read(),m=read();
for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read();
for(int i=1;i<(1<<n);i++) num[i]=num[i>>1]+(i&1);
for(int i=0;i<(1<<n);i++)
{
for(int j=1;j<=m;j++)
{
int u=e[j].u,v=e[j].v;
if(((i>>u-1)&1)&&((i>>v-1)&1)) sum[i]++;
}
}
for(int i=1;i<(1<<n);i++)
{
if(num[i]==1) g[0][i]=1; //f[i]不连通,g[i]连通
else f[0][i]=1;
}
C[0][0]=1;
for(int i=1;i<=100;i++)
{
C[i][0]=1;
for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
}
for(int s=1;s<(1<<n);s++)
{
for(int i=1;i<=sum[s];i++)
{
int u=s&-s;
for(int t=(s-1)&s;t;t=(t-1)&s)
{
if(!(t&u)) continue;
for(int j=0;j<=min(i,sum[t]);j++) f[i][s]+=g[j][t]*C[sum[s^t]][i-j];
}
g[i][s]=C[sum[s]][i]-f[i][s];
}
}
double ans=0;
for(int i=0;i<m;i++) ans+=1.0*f[i][(1<<n)-1]/C[m][i];
ans/=(m+1);
printf("%.6lf",ans);
return 0;
}
/*by DT_Kang*/