【ZJOI2015】【期望dp】【状压dp】地震后的幻想乡

8 篇文章 0 订阅
6 篇文章 0 订阅

【题目描述】
傲娇少女幽香是一个很萌很萌的妹子,而且她非常非常地有爱心,很喜欢为幻想乡的人们做一些自己力所能及的事情来帮助他们。

这不,幻想乡突然发生了地震,所有的道路都崩塌了。现在的首要任务是尽快让幻想乡的交通体系重新建立起来。幻想乡一共有 n 个地方,那么最快的方法当然是修复 n−1 条道路将这 n 个地方都连接起来。 幻想乡这 n 个地方本来是连通的,一共有 m 条边。现在这 m 条边由于地震的关系,全部都毁坏掉了。每条边都有一个修复它需要花费的时间,第 i 条边所需要的时间为 ei 。地震发生以后,由于幽香是一位人生经验丰富,见得多了的长者,她根据以前的经验,知道每次地震以后,每个 ei 会是一个 0 到 1 之间均匀分布的随机实数。并且所有 ei 都是完全独立的。

现在幽香要出发去帮忙修复道路了,她可以使用一个神奇的大魔法,能够选择需要的那 n−1 条边,同时开始修复,那么修复完成的时间就是这 n−1 条边的 ei 的最大值。当然幽香会先使用一个更加神奇的大魔法来观察出每条边 ei 的值,然后再选择完成时间最小的方案。 幽香在走之前,她想知道修复完成的时间的期望是多少呢?

【输入】
第一行两个数 n,m,表示地方的数量和边的数量。其中点从 1 到 n 标号。

接下来 m 行,每行两个数 a,b,表示点 a 和点 b 之间原来有一条边。 这个图不会有重边和自环

【输出】
一行输出答案,四舍五入保留6位小数。
提示:
对于 n 个 [0,1] 之间的随机变量 x1,x2,…,xn,第 k 小的那个的期望值是 k/(n+1)。

数据范围:
对于所有数据:n≤10, m≤n(n−1)/2, n,m≥1
对于 15% 的数据:n≤3
另有 15% 的数据:n≤10,m=n
另有 10%的数据:n≤10,m=n(n−1)/2
另有 20%的数据:n≤5
另有 20%的数据:n≤8

【思路】

分析题意:给定一个边权在[0,1]的无向图,求其最小生成树的最大边期望边权。我们考虑怎样利用提示:如果我们知道了最小生成树最大边在所有边中的期望排名,那么就可以利用提示计算答案了。设排名期望为E(x),则:
E ( x ) = ∑ i = 1 p ( x > = i ) E(x)=\sum_{i=1}p(x>=i) E(x)=i=1p(x>=i)
这是一个非常常用的公式,它其实就是把统计期望的顺序变了一下。我们考虑如何通过计数解决上面这个式子:
最大边排名期望大于等于i的概率就是已经加入了i-1条边的图没有联通的概率。
不过你可能会疑惑,我们应该计算的是树而不是图,其实这两者的概率是一样的。考虑加入最大边时图(树)联通,它们的方案数是对应的。设 f [ s ] [ i ] f[s][i] f[s][i]表示加入了i条边,图中的点集s不联通的方案数。如果我们计算出每种点集包含的边数cnt[s](可以暴力,可以FMT ),我们会发现 ( i c n t [ s ] ) − f [ s ] [ i ] (^{cnt[s]}_{i})-f[s][i] (icnt[s])f[s][i]就是点集s联通的方案数。考虑递推:
f [ s ] [ i ] = ∑ ( ( j c n t [ u ] ) − f [ u ] [ j ] ) ∗ ( i − j c n t [ s − u ] ) f[s][i]=\sum ((^{cnt[u]}_{j})-f[u][j])*(^{cnt[s-u]}_{i-j}) f[s][i]=((jcnt[u])f[u][j])(ijcnt[su])
即枚举一个点集u来更新当前状态。当然,为不重不漏,我们需要确保u和s有一个确定的公共点,而我们所要枚举的正是这个公共点所在连通块的状态及边数。结合第一个期望的公式和提示的信息,我们得到(U为全集):
a n s = ( 1 / ( m + 1 ) ) ∗ ∑ i = 0 m f [ U ] [ i ] / ( i c n t [ U ] ) ans=(1/(m+1))*\sum_{i=0}^{m}f[U][i]/(^{cnt[U]}_{i}) ans=(1/(m+1))i=0mf[U][i]/(icnt[U])
注意从0开始枚举,原因由黑体字可知。
代码:

#include<bits/stdc++.h>
#define re register
using namespace std;
const int N=11;
inline int red(){
    int data=0;bool w=0; char ch=0;
    ch=getchar();
    while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
    if(ch=='-') w=1,ch=getchar();
    while(ch>='0' && ch<='9') data=(data<<3)+(data<<1)+ch-'0',ch=getchar();
    return w?-data:data;
}
int n,m,a,b,cnt[1<<N|1],S;double ans=0;
long long dp[1<<N|1][55],c[55][55];
inline long long C(int n,int m){
	if(n<m)return 0;
	if(n==m||m==0)return 1;
	if(m==1)return n;
	return c[n][m]?c[n][m]:c[n][m]=C(n-1,m-1)+C(n-1,m);
}
int main(){
	n=red();m=red();S=1<<n;
	for(int re i=1;i<=m;i++)cnt[(1<<(red()-1))|(1<<(red()-1))]=1;
	for(int re i=0;i<n;i++)
		for(int re j=0;j^S;j++)
			if(j&(1<<i))cnt[j]+=cnt[j^(1<<i)];//子集前缀和卡常,看不懂请忽略,自行yy暴力
	for(int re s=1;s^S;++s){
		for(int re i=0;i<=cnt[s];++i)
			for(int re u=(s-1)&s;u;u=(u-1)&s)
			if(u&(s&(-s)))
				for(int re j=0;j<=min(i,cnt[u]);++j)
					dp[s][i]+=(C(cnt[u],j)-dp[u][j])*C(cnt[s^u],i-j);
	}for(int re i=0;i<=m;i++)ans+=1.0*dp[S-1][i]/C(m,i);
	printf("%.6f\n",ans/(m+1));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值