在题目中,假设有恰好k条边使图联通,那么答案就是k/(m+1)。
如果我们令f[i][j]表示子集为i,已经选了j条边时集合不连通的方案数,令g[i][j]表示连通的方案数,那么显然可以得到g[i][j]+f[i][j]=C(m,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