[loj2136]地震后的幻想乡

考虑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',k\in 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'$联通且让$k\in 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 }
View Code

 

posted @ 2020-10-20 15:06  PYWBKTDA  阅读(87)  评论(0编辑  收藏  举报