考虑kruskal的过程:对$n$条边随机排列(排序),令$k$表示前$k$条边恰好能使图联通,根据题目的提示,即$E(frac{k}{m+1})=frac{E(k)}{m+1}$
设$p(k)$表示选择$k$条边能使图联通(不是恰好)的方案数,则有$E(k)=sum_{i=n-1}^{m}(frac{p(i)}{c(m,i)}-frac{p(i-1)}{c(m,i-1)})i$
考虑dp,设$f[S][i]$和$g[S][i]$分别表示选择$i$条边(仅考虑两点都在$S$中的边)使得子集$S$联通/不连通的方案数,由于,计算$g[S][i]$
枚举子集$S'in S$,那么即$g[S][i]=sum_{S',kin S'}sum_{j=0}^{i}c(m,i-j)cdot f[S'][j]$(这里的$m$是指两点都在$C_{S}S'$中的边数量),同时由于$f[S][i]+g[S][i]=c(m,i)$(这里的$m$是两点都在$S$中的边数),可以算出$f[S][i]$
解释一下:强制$S'$与$C_{S}S'$不连通,同时为了去重,我们强制$S'$联通且让$kin S'$($k$为$S$中的某个任意元素)
显然$p(i)=f[V][i]$,即可算出答案,时间复杂度通过枚举子集的技巧可以优化到$o(m^{2}cdot 3^{n})$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 2005 4 #define M 105 5 int n,m,x,y,tot[N]; 6 long long c[M][M],g[N][M],f[N][M]; 7 double ans,p[M]; 8 int main(){ 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=m;i++){ 11 scanf("%d%d",&x,&y); 12 int p=((1<<x-1)|(1<<y-1)); 13 for(int j=0;j<(1<<n);j++)tot[j]+=((p&j)==p); 14 } 15 for(int i=0;i<=m;i++)c[i][0]=c[i][i]=1; 16 for(int i=1;i<=m;i++) 17 for(int j=1;j<i;j++)c[i][j]=c[i-1][j]+c[i-1][j-1]; 18 for(int i=1;i<(1<<n);i++){ 19 int p=i-(i&(i-1)); 20 for(int j=0;j<=tot[i];j++){ 21 for(int k=i;k;k=((k-1)&i)) 22 if (k&p) 23 for(int l=0;l<=tot[i^k];l++)g[i][j]+=c[tot[i^k]][l]*f[k][j-l]; 24 f[i][j]=c[tot[i]][j]-g[i][j]; 25 } 26 } 27 for(int i=n-1;i<=m;i++)p[i]=1.0*f[(1<<n)-1][i]/c[m][i]; 28 ans=p[n-1]*(n-1); 29 for(int i=n;i<=m;i++)ans+=(p[i]-p[i-1])*i; 30 printf("%.6f",ans/(m+1)); 31 }