Problem
题意简述:给定一个(n)((nleq 10))个点(m)条边的无向图,每条边的权值为一个(0)到(1)之间的连续随机变量,求图的最小生成树中最大边的期望权值
Solution
(m)个范围([0,1])之间的随机变量中,第(i)大的值期望为(frac i{m+1})(这个式子我只会证最大值期望为(frac m{m+1}),但不会证第(i)大的期望,如果有哪位神犇会证,请赐教)
upd:现在我会证第(i)大的期望了☞博客链接☜
有了这个东西,再考虑到原题是要求联通即可,发现(kruskal)算法正好符合
将边排序后加入,若加入到第(i)条边时整张图连通,则答案为(frac i{m+1});若期望第(i')次连通整张图,则答案为(frac {i'}{m+1})
设(f[S][i])表示用了(i)条边不能使(S)联通的方案数,(g[S][i])表示用了(i)条边能使(S)联通的方案数,(C[S])表示点集(S)内部道路数量
[f[S][j]=sum g[s][i]cdot inom {C[S-s]}{j-i}
]
[g[S][j]=inom {C[S]}i-f[S][j]
]
考虑到若第(i)条边加入后仍未连通,则会对后面都造成影响(类似整数期望公式的一种统计方法)
[Ans=frac 1{m+1}sum_{i=0}^mfrac {f[A][i]}{inom {C[A]}{i}}
]
最后记得在枚举集合的时候要定下一个点强制选择,否则可能会拓扑序紊乱(代码里的做法就是 S+=2
而不是 ++S
,这样枚举的集合必定含有(1)号节点)
Code
#include <bits/stdc++.h>
typedef long long ll;
#define rg register
template <typename _Tp> inline _Tp read(_Tp&x){
char c11=getchar(),ob=0;x=0;
while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')ob=1,c11=getchar();
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}
const int N=11,M=60,MS=1<<N;
ll c[M][M],C[MS],f[MS][M],g[MS][M];
int n,m,A,bin[N];
void init();void work();void print();
int main(){init();work();print();return 0;}
void work(){
for(rg int S=1;S<bin[n];S+=2)
for(rg int j=0;j<=C[A];++j){
for(rg int s=(S-1)&S;s;s=(s-1)&S)
for(rg int i=0;i<=j;++i)
f[S][j]+=g[s][i]*c[C[S-s]][j-i];
g[S][j]=c[C[S]][j]-f[S][j];
}
}
void print(){
double ans(0.0);
for(rg int i=0;i<=C[A];++i)
ans+=1.0*f[A][i]/c[C[A]][i];
printf("%.6lf
",ans/(C[A]+1));
}
void init(){
read(n),read(m);A=(1<<n)-1;
int x,y;bin[0]=1;
for(rg int i=1;i<=n;++i)bin[i]=bin[i-1]<<1;
for(rg int i=0;i<=m;++i){
c[i][0]=1;
for(int j=1;j<=i;++j)
c[i][j]=c[i-1][j-1]+c[i-1][j];
}
while(m--){
read(x),read(y);--x,--y;
for(rg int S=1;S<bin[n];++S)
if( S&bin[x] and S&bin[y] )
++C[S];
}
}