题解-ZJOI2015地震后的幻想乡

Problem

bzoj & 洛谷

题意简述:给定一个(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];
	}
}
原文地址:https://www.cnblogs.com/penth/p/9735953.html