地震后的幻想乡

题意:给定一张图,每条边的边权是一个 0~1 的随机实数,求最大边最小的生成树的期望权值。

n n n [ 0 , 1 ] [0,1] [0,1] 的随机变量,第 k k k 大的期望是 k / ( n + 1 ) k/(n+1) k/(n+1)

a n s × ( m + 1 ) = ∑ k P ( 加 入 k 条 边 后 图 恰 好 连 通 ) × k ans\times (m+1)=\sum_{k}P(加入 k 条边后图恰好连通)\times k ans×(m+1)=kP(k)×k

它等于 ∑ k = 1 m P ( 加 入 ≥ k 条 边 后 图 连 通 ) \sum_{k=1}^{m}P(加入 \geq k 条边后图连通) k=1mP(k),又等价于 ∑ k P ( 加 入 k − 1 条 边 后 图 不 连 通 ) \sum_kP(加入k-1 条边后图不连通) kP(k1)

于是现在我们要求选出 i 条边,不连通的概率 → \rightarrow 方案数(保证精度?)。

f[i][s][0/1] 表示在 s 集合里选了 i 条边,s 这个集合里的点 连通/不连通 的方案数。枚举子集转移。

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define ld long double
using namespace std;
struct edge {
	int u,v;
}e[1000];
ll f[50][1<<10],g[50][1<<10],C[120][120];
int sum[1<<10],num[1<<10];
int read()
{
	int x=0;char c=getchar(),flag='+';
	while(!isdigit(c)) flag=c,c=getchar();
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	return flag=='-'?-x:x;
}
int main()
{
	int n=read(),m=read();
	for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read();
	for(int i=1;i<(1<<n);i++) num[i]=num[i>>1]+(i&1); 
	for(int i=0;i<(1<<n);i++)
	{
		for(int j=1;j<=m;j++)
		{
			int u=e[j].u,v=e[j].v;
			if(((i>>u-1)&1)&&((i>>v-1)&1)) sum[i]++; 
		}
	}
	for(int i=1;i<(1<<n);i++)
	{
		if(num[i]==1) g[0][i]=1;	//f[i]不连通,g[i]连通 
		else f[0][i]=1;
	}
	C[0][0]=1;
	for(int i=1;i<=100;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j];
	}
	for(int s=1;s<(1<<n);s++)
	{
		for(int i=1;i<=sum[s];i++)
		{
			int u=s&-s;
			for(int t=(s-1)&s;t;t=(t-1)&s)
			{
				if(!(t&u)) continue;
				for(int j=0;j<=min(i,sum[t]);j++) f[i][s]+=g[j][t]*C[sum[s^t]][i-j];
			}
			g[i][s]=C[sum[s]][i]-f[i][s];
		}
	}
	double ans=0;
	for(int i=0;i<m;i++) ans+=1.0*f[i][(1<<n)-1]/C[m][i];
	ans/=(m+1);
	printf("%.6lf",ans);
	return 0;
}
/*by DT_Kang*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值