bzoj2878 [NOI2012] 迷失游乐园 期望+基环树dp

17 篇文章 0 订阅
5 篇文章 0 订阅

题目描述

放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩。

进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点、m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1)。小Z现在所在的大门也正好是一个景点。小Z不知道什么好玩,于是他决定,从当前位置出发,每次随机去一个和当前景点有道路相连的景点,并且同一个景点不去两次(包括起始景点)。贪玩的小Z会一直游玩,直到当前景点的相邻景点都已经访问过为止。

小Z所有经过的景点按顺序构成一条非重复路径,他想知道这条路径的期望长度是多少?

小Z把游乐园的抽象地图画下来带回了家,可是忘了标哪个点是大门,他只好假设每个景点都可能是大门(即每个景点作为起始点的概率是一样的)。同时,他每次在选择下一个景点时会等概率地随机选择一个还没去过的相邻景点。

【评分方法】本题没有部分分,你程序的输出只有和标准答案的差距不超过0.01时,才能获得该测试点的满分,否则不得分。

输入输出格式

输入格式:

第一行是两个整数n和m,分别表示景点数和道路数。 接下来行,每行三个整数Xi, Yi, Wi,分别表示第i条路径的两个景点为Xi, Yi,路径长Wi。所有景点的编号从1至n,两个景点之间至多只有一条道路。

输出格式:

共一行,包含一个实数,即路径的期望长度,保留五位小数。

输入输出样例

输入样例#1:  复制
4 3
1 2 3
2 3 1
3 4 4
输出样例#1:  复制
6.00000000

说明

【样例解释】样例数据中共有6条不同的路径: 路径 长度 概率

路径 长度  概率 
1-->4  8   1/4 
2-->1  3   1/8 
2-->4  5   1/8 
3-->1  4   1/8 
3-->4  4   1/8 
4-->1  8   1/4 

因此期望长度 = 8/4 + 3/8 + 5/8 + 4/8 + 4/8 + 8/4 = 6.00

【评分方法】本题没有部分分,你程序的输出只有和标准答案的差距不超过0.01时,才能获得该测试点的满分,否则不得分。

【数据规模和约定】对于100%的数据,1 <= Wi <= 100。

测试点编号 n m 备注 1 n=10 m = n-1 保证图是链状 2 n=100 只有节点1的度数大于2 3 n=1000 / 4 n=100000 / 5 n=100000 / 6 n=10 m = n / 7 n=100 环中节点个数<=5 8 n=1000 环中节点个数<=10

9 n=100000 环中节点个数<=15

10 n=100000 环中节点个数<=20


题解:考虑只有一棵树,那么down[x]和up[x]分别表示向上走和向下走的期望,然后先dfs一遍求down,down[i]=sigma(down[j]+len[i][j])/du[i](j是i的儿子),也就是期望加起来再加上边的长度再除以总情况数。

然后再求up[x]。up[x]=len[fa[x]][x]+(up[fa[x]]+down[fa[x]]*du[fa[x]]-down[x]-len[fa[x]][x])/du[fa[x]]。首先我们要走x到父亲的一段,然后再加上父亲的up+父亲的down*父亲的度数,但是这样会把x子树算上,所以我们要减去down[x]和len[fa[x]][x],最后这个整体再除以总数(也就是乘概率),就是up[x]。

那么最后所有节点的ans除以自己的度数(也就是乘概率),再全部加起来除以n,就是最后的答案了。

但是我们有一个环该怎么办呢。

首先这个环导致树变成了len(环)个小树+一个大环,所以我们可以直接对所有小树求down,up就比较麻烦了,我们先对环求出来g数组,表示从这个点开始走的期望,因为环的大小很小,所以我们枚举rt,然后直接dp一遍,g[i]=(g[to[x]]+len[to[x]][x])/(du[x]+1),算完之后,我们再把down计入到贡献中,直接加权即可,但是要注意度数为0的情况,要强制置1。如果下一步就走到了rt,那么我们就不要把g计算到贡献中,因为我们钦定了rt,不能出现走回去的情况;如果当前点就是rt,那么不要计算down的贡献直接return,因为现在我们直接就钦定了up是往rt走的。

算完g和down对答案的贡献之后,我们把环上所有的du都+=2,因为环上有两个出边(前面不加是因为计算的是down的贡献,而g不需要度数来计算),然后我们枚举环上每个点,进行up的计算。

具体看代码吧。

自带大常数(逃

#include <bits/stdc++.h>
using namespace std;
struct nod{int to;double len;};
vector<nod>e[100005];
int x,y,z,n,m,xi,yi,wi,cir[100005],vis[100005],fa[100005],du[100005],rt,tim;
double f[100005],d[100005],tp[100005],g[100005],ans;
void dfs1(int x)
{
	vis[x]=1;
	for(int i=0;i<e[x].size();i++)
		if(!vis[e[x][i].to]&&!cir[e[x][i].to])
			dfs1(e[x][i].to),du[x]++,d[x]+=f[e[x][i].to]+e[x][i].len;
	if(du[x]) f[x]=d[x]/(double)du[x];
	if(x!=rt) du[x]++;
}
void dfs2(int x)
{
	vis[x]=1;
	for(int i=0;i<e[x].size();i++)
		if(!vis[e[x][i].to]&&!cir[e[x][i].to])
			d[e[x][i].to]+=(d[x]-f[e[x][i].to]-e[x][i].len)/max(1,du[x]-1)+e[x][i].len,dfs2(e[x][i].to);
}
void dfs3(int x)
{
	vis[x]=++tim;
	for(int i=0,j;i<e[x].size();i++)
		if(e[x][i].to!=fa[x])
		{
			if(!vis[e[x][i].to]) fa[e[x][i].to]=x,dfs3(e[x][i].to);
			else if(vis[e[x][i].to]<vis[x]) for(cir[e[x][i].to]=1,j=x;j!=e[x][i].to;j=fa[j]) cir[j]=1;
		}
}
void dfs4(int x,int fa)
{
	bool flag=0;g[x]=0;
	for(int i=0;i<e[x].size();i++)
		if(e[x][i].to!=rt&&e[x][i].to!=fa&&cir[e[x][i].to])
			flag=1,dfs4(e[x][i].to,x),g[x]+=g[e[x][i].to]+e[x][i].len;
	if(x==rt) return;
	int k=du[x];k?k:k++;
	if(!flag) g[x]=d[x]/(double)k;
	else k=du[x]+1,g[x]=(g[x]+d[x])/(double)k;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),e[x].push_back((nod){y,z}),e[y].push_back((nod){x,z});
	if(m==n-1) rt=1,dfs1(1),memset(vis,0,sizeof vis),dfs2(1);
	else
	{
		dfs3(1);memset(vis,0,sizeof vis);
		for(int i=1;i<=n;i++) if(cir[i]) rt=i,dfs1(i);
		for(int i=1;i<=n;i++) if(cir[i]) rt=i,dfs4(i,0),tp[i]=g[i];
		memset(vis,0,sizeof(vis));
		for(int i=1;i<=n;i++) if(cir[i]) du[i]+=2,d[i]+=tp[i];
		for(int i=1;i<=n;i++) if(cir[i]) rt=i,dfs2(i);
	}
	for(int i=1;i<=n;i++) ans+=d[i]/(double)du[i];
	printf("%.5lf\n",ans/(double)n);
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值