标签:har mes 就是 连通 style tar algo size def
由提示可以知道,如果把图中的边从小到大依次加入,在加入第 $k$ 条边时图恰好联通,那么期望花费为 $\frac{k}{m+1}$
注意到期望花费和加入边数成正比,发现可以看成每一条加入后不使图联通的边的贡献之和,每条不使图联通的边的贡献即为 $\frac{1}{m+1}$
那么如果能算出 $f[i]$ 表示加入第 $i$ 条边时图仍然不连通的概率,那么它对答案的贡献就是 $\frac {1}{m+1}$,期望贡献即为 $\frac{f[i]}{m+1}$
那么枚举每一条不使图联通的边,可以得到 $ans=\frac{1}{m+1}\sum_{k=0}^{m}f[k]$(注意这里有算到第 $0$ 条边的贡献)
然后现在的问题就是求这个东西的概率,考虑求出合法方案数,然后除以总方案数即可得到概率
设 $f[S][i]$ 表示当前点集为 $S$ ,加入了 $i$ 条边仍然不联通的方案数,同理设 $g[S][i]$ 表示联通的方案数
那么显然有 $f[S][i]+g[S][i]=\binom{D_S}{i}$ ,其中 $D_S$ 表示点集 $S$ 的总边数
现在考虑 $f$ 的转移,显然考虑枚举这个不连通块中的某个联通块子集,那么容易想到转移 $f[S][i]=\sum_{T \in S}\sum_{j=0}^{min(i,D_T)} g[T][j]\binom{D_{S-T}}{i-j}$
但是发现这样对于 $f[S][i]$ 的每个方案都多算了那个方案中联通块个数次
然后这里就有一个神仙操作,钦定某个点一定在 $T$ 中,那么这样就不会重复算到一种方案了(因为这个方案的其他联通块都不会枚举到了)
然后算完 $f[S][i]$ 那么 $g[S][i]=\binom{D_S}{i}-f[S][i]$
最后 $ans=\frac{1}{m+1}\sum_{k=0}^{m}\frac{f[U][k]}{\binom{m}{k}}$,显然 $U$ 表示全集,$\binom{m}{k}$ 就是总方案
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; typedef double db; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<‘0‘||ch>‘9‘) { if(ch==‘-‘) f=-1; ch=getchar(); } while(ch>=‘0‘&&ch<=‘9‘) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=11,M=107; int n,m,cnt[(1<<N)+7],a[M],b[M]; ll C[M][M],f[(1<<N)+7][M],g[(1<<N)+7][M]; int main() { n=read(),m=read(); for(int i=1;i<=m;i++) a[i]=read()-1,b[i]=read()-1; int mx=(1<<n)-1; for(int i=1;i<=mx;i++) for(int j=1;j<=m;j++) if((i>>a[j])&1 && (i>>b[j])&1) cnt[i]++; for(int i=0;i<=m;i++) { C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=C[i-1][j]+C[i-1][j-1]; } for(int o=1;o<=mx;o++) for(int j=0;j<=cnt[o];j++) { for(int op=(o-1)&o;op;op=(op-1)&o) if(op&(o&-o)) for(int k=0;k<=min(j,cnt[op]);k++) f[o][j]+=g[op][k]*C[cnt[o^op]][j-k]; g[o][j]=C[cnt[o]][j]-f[o][j]; } db ans; for(int i=0;i<=m;i++) ans+=1.0*f[mx][i]/C[m][i]; ans/=m+1; printf("%.6lf\n",ans); return 0; }
标签:har mes 就是 连通 style tar algo size def
原文地址:https://www.cnblogs.com/LLTYYC/p/11707488.html