bzoj3925 地震后的幻想乡 子集Dp

本文介绍了一种基于图论的概率算法,通过计算不同边数量下图的联通性方案数来求解期望值问题。该算法定义了两种状态,即连通和不连通,并给出了详细的递推公式及AC代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       在题目中,假设选了恰好k条边使图联通,那么答案就是k/(m+1)。

       如果我们令f[i][j]表示子集为i,已经选了j条边时集合不连通的方案数,令g[i][j]表示连通的方案数,那么显然可以得到g[i][j]+f[i][j]=C(mi,j)。mi为子集i的诱导子图的边的数量

       然后计算f[i],可以利用任意一个i中的点x,然后用包含x的连通块连通的方案数*剩下的边都不在包含x的连通块中的概率得到。

       最后统计答案时,考虑添加i条边不连通时,相比于i-1条边不连通,期望的最小瓶颈生成树增加k/(m+1)-(k-1)/(m+1)=1/m+1,再乘上概率就是期望的答案增加的值。因此这时给答案带来的贡献为f[all][i]/C(m,i)*1/(m+1)。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
#define N 2105
using namespace std;

int n,m,e[105],bin[105],sz[N],sum[N];
ll c[105][105],f[N][105],g[N][105];
int main(){
	scanf("%d%d",&n,&m); int i,j;
	bin[0]=1; for (i=1; i<=n; i++) bin[i]=bin[i-1]<<1;
	for (i=1; i<=m; i++){
		int x,y; scanf("%d%d",&x,&y);
		e[x]|=bin[y-1]; e[y]|=bin[x-1];
		for (j=1; j<bin[n]; j++)
			if ((j&bin[x-1]) && (j&bin[y-1])) sum[j]++;
	}
	c[0][0]=1;
	for (i=1; i<=m; i++){
		c[i][0]=1;
		for (j=1; j<=i; j++) c[i][j]=c[i-1][j]+c[i-1][j-1];
	}
	for (i=1; i<bin[n]; i++){
		sz[i]=sz[i>>1]+(i&1);
		if (sz[i]==1){ g[i][0]=1; continue; }
		int t=i&-i,x,y;
		for (j=(i-1)&i; j; j=(j-1)&i) if (j&t){
			int k=i^j;
			for (x=0; x<=sum[j]; x++)
				for (y=0; y<=sum[k]; y++)
					f[i][x+y]+=g[j][x]*c[sum[k]][y];
		}
		for (j=0; j<=sum[i]; j++) g[i][j]=c[sum[i]][j]-f[i][j];
	}
	double ans=0;
	for (i=0; i<=m; i++)
		ans+=(double)f[bin[n]-1][i]/c[m][i];
	printf("%.6f\n",ans/(m+1));
	return 0;
}


by lych

2016.2.29

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值