在题目中,假设选了恰好k条边使图联通,那么答案就是k/(m+1)。
如果我们令f[i][j]表示子集为i,已经选了j条边时集合不连通的方案数,令g[i][j]表示连通的方案数,那么显然可以得到g[i][j]+f[i][j]=C(mi,j)。mi为子集i的诱导子图的边的数量
然后计算f[i],可以利用任意一个i中的点x,然后用包含x的连通块连通的方案数*剩下的边都不在包含x的连通块中的概率得到。
最后统计答案时,考虑添加i条边不连通时,相比于i-1条边不连通,期望的最小瓶颈生成树增加k/(m+1)-(k-1)/(m+1)=1/m+1,再乘上概率就是期望的答案增加的值。因此这时给答案带来的贡献为f[all][i]/C(m,i)*1/(m+1)。
AC代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 2105
using namespace std;
int n,m,e[105],bin[105],sz[N],sum[N];
ll c[105][105],f[N][105],g[N][105];
int main(){
scanf("%d%d",&n,&m); int i,j;
bin[0]=1; for (i=1; i<=n; i++) bin[i]=bin[i-1]<<1;
for (i=1; i<=m; i++){
int x,y; scanf("%d%d",&x,&y);
e[x]|=bin[y-1]; e[y]|=bin[x-1];
for (j=1; j<bin[n]; j++)
if ((j&bin[x-1]) && (j&bin[y-1])) sum[j]++;
}
c[0][0]=1;
for (i=1; i<=m; i++){
c[i][0]=1;
for (j=1; j<=i; j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
}
for (i=1; i<bin[n]; i++){
sz[i]=sz[i>>1]+(i&1);
if (sz[i]==1){ g[i][0]=1; continue; }
int t=i&-i,x,y;
for (j=(i-1)&i; j; j=(j-1)&i) if (j&t){
int k=i^j;
for (x=0; x<=sum[j]; x++)
for (y=0; y<=sum[k]; y++)
f[i][x+y]+=g[j][x]*c[sum[k]][y];
}
for (j=0; j<=sum[i]; j++) g[i][j]=c[sum[i]][j]-f[i][j];
}
double ans=0;
for (i=0; i<=m; i++)
ans+=(double)f[bin[n]-1][i]/c[m][i];
printf("%.6f\n",ans/(m+1));
return 0;
}
by lych
2016.2.29