【BZOJ 2878】 [Noi2012]迷失游乐园

2878: [Noi2012]迷失游乐园

Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge

Submit: 415  Solved: 283

[Submit][Status]

Description

放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩。进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点、m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1)。小Z现在所在的大门也正好是一个景点。小Z不知道什么好玩,于是他决定,从当前位置出发,每次随机去一个和当前景点有道路相连的景点,并且同一个景点不去两次(包括起始景点)。贪玩的小Z会一直游玩,直到当前景点的相邻景点都已经访问过为止。小Z所有经过的景点按顺序构成一条非重复路径,他想知道这条路径的期望长度是多少?小Z把游乐园的抽象地图画下来带回了家,可是忘了标哪个点是大门,他只好假设每个景点都可能是大门(即每个景点作为起始点的概率是一样的)。同时,他每次在选择下一个景点时会等概率地随机选择一个还没去过的相邻景点。

Input

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

Output

共一行,包含一个实数,即路径的期望长度。

Sample Input

4 3

1 2 3

2 3 1

3 4 4

Sample Output

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

HINT

Source

鸣谢Ljcc提供SPJ

树dp+基环树。

如果输入是一棵树的话(m=n-1),用O(n)就可以解决:

1.首先随便选择一个点做根。

2.Dfs

si表示i的儿子结点;

f[i]表示i结点向儿子走的期望长度;

d[i]=sigma(f[si]+edge(si,i));

du[i]表示i结点的度数,即儿子的个数+1(父亲);

显然,f[i]=d[i]/(du[i]-1)。

我们做树形dp就可以把f[],d[],du[]数组都求出来。

3.Dfs2

那么接下来要求的是i结点走向任意一个叶子结点的期望长度,已经求了走向儿子的期望长度,还需求的是走向父亲的期望长度p[i]。

p[root]=f[root]。

接下来dfs,dfs(i)的时候他的父亲x的p[x]已经求出,那么:

d[i]+=(d[x]-y[i]-edge(x,i))/(du[x]-1)+edge(x,i)

p[i]=d[i]/du[i]。

一次dfs之后所有点的p[]就求出来了。

如果是m=n呢?

那么他是一棵基环树即这棵树中有且仅有一个环。

1.Findcir

先找到这个环。

2.Dfs

以环上的每一个点为根,做m=n-1时的第二步求出每个点的d[],f[]。

3.Calc

因为有环,所以环上每个结点的f[i]并不等于p[i]。

那么我们需要枚举环上的每个点,计算从这个点出发沿着环走的期望长度。

注意环上的点度数要加2。

4.Dfs2

此时环上每个点的d[i]/du[i]=p[i],然后按照m=n-1时的第二步计算非环上的点的p[i]即可。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#define M 100005
#define ld long double
using namespace std;
int fa[M],c[M],v[M],tot=0,n,m,du[M],now=0,h[M],root;
ld g[M],f[M],d[M],gg[M];
struct edge
{
	int y,ne,l;
}e[M*5];
void Addedge(int x,int y,int l)
{
	tot++;
	e[tot].y=y;
	e[tot].ne=h[x];
	e[tot].l=l;
	h[x]=tot;
}
void Dfs(int x)
{
	d[x]=f[x]=0.000;
	v[x]=1;
	du[x]=0;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (v[y]||c[y]) continue;
		Dfs(y);
		du[x]++;
		d[x]=d[x]+f[y]+(ld)e[i].l;
	}
	if (du[x]) f[x]=d[x]/(ld)du[x];
	if (x!=root) du[x]++;
}
void Dfs2(int x)
{
	v[x]=1;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (v[y]||c[y]) continue;
		int k=du[x]-1;
		if (!k) k++;
		d[y]=d[y]+(d[x]-f[y]-(ld)e[i].l)/(ld)(k)+(ld)e[i].l;
		Dfs2(y);
	}
}
void Findcir(int x)
{
	v[x]=++now;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (!v[y])
		{
			fa[y]=x;
			Findcir(y);
		}
		else if (y!=fa[x]&&v[y]<v[x])
		{
			c[y]=1;
			while (x!=y)
			{
				c[x]=1;
				x=fa[x];
			}
			return;
		}
	}
}
void Calc(int x,int fa)
{
	bool last=true;
	g[x]=0.000;
	for (int i=h[x];i;i=e[i].ne)
	{
		int y=e[i].y;
		if (y!=root&&y!=fa&&c[y])
		{
			last=false;
			Calc(y,x);
			g[x]=g[x]+g[y]+(ld)e[i].l;
		}
	}
	int k=du[x];
	if (!k) k++;
	if (last) g[x]=d[x]/(ld)k;
	else
	{
		k=du[x]+1;
		if (x!=root)
			g[x]=(g[x]+d[x])/(ld)k;
		else
		{
			return;
		}
	}
}
int main()
{
    scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
	{
		int x,y,l;
		scanf("%d%d%d",&x,&y,&l);
		Addedge(x,y,l);
		Addedge(y,x,l);
	}
	if (n==m+1)
	{
		root=1;
		Dfs(1);
		for (int i=1;i<=n;i++)
			v[i]=0;
		Dfs2(1);
	}
	else
	{
		Findcir(1);
		for (int i=1;i<=n;i++)
			v[i]=0;
		for (int i=1;i<=n;i++)
			if (c[i]) root=i,Dfs(i);
		for (int i=1;i<=n;i++)
			if (c[i]) root=i,Calc(i,0),gg[i]=g[i];
		for (int i=1;i<=n;i++)
			if (c[i]) du[i]+=2,d[i]+=gg[i];
		for (int i=1;i<=n;i++)
			v[i]=0;
		for (int i=1;i<=n;i++)
			if (c[i]) Dfs2(i);
	}
	double ans=0.000;
	for (int i=1;i<=n;i++)
		ans=ans+d[i]/(ld)du[i];
	printf("%.5lf\n",ans/(double)n);
	return 0;
}

感悟:

1.RE:是在Findcir中对v[]数组没有区分,找过的赋值为1,那么在找环的时候就会出错:找到环后并不会立刻退出子程序而是继续找,那么就会又找到刚刚找过的那个环,然后就永远出不去了。。

后来WA了好几次(找了半天才找到问题):

在Calc中直接更新了d[x],那么在计算其他环上的点时,应该用的是未被更新的d[x]!!!所以结果就偏大了。。

2.对于基环树问题,一般环上的点数不会很多,所以我们可以考虑枚举环上的每一个点来做~

时间: 2024-10-13 05:28:30

【BZOJ 2878】 [Noi2012]迷失游乐园的相关文章

BZOJ 2878([Noi2012]迷失游乐园-树形DP+环加外向树+期望DP+vector的erase)

2878: [Noi2012]迷失游乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1).小Z现在所在的大门也正好是

BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm&

BZOJ 2878: [Noi2012]迷失游乐园

Writing now. 1 /************************************************************** 2 Problem: 2878 3 User: zrts 4 Language: C++ 5 Result: Accepted 6 Time:588 ms 7 Memory:10748 kb 8 ****************************************************************/ 9 10 #i

BZOJ 2878 [Noi2012]迷失游乐园 树形期望DP+基环树

题意:链接 方法:树形期望DP+基环树 解析: 首先先看前50%的数据 是一棵树 那么我们可以搞树形DP 然后设几个正常的数组 sum[i]代表i走i的子节点的期望的和. down[i]代表从底下走到i的期望. size[i]代表i的儿子个数 up[i]代表从i往上走的期望 然后就可以推式子了 显而易见地可以推出来up的式子 然后有一些奇怪的关于根节点的特判,注意一下就OK了. 然后后50% 我们发现它是一个基环树? 那么首先可以乱搞出来环上的点,然后记录一下这个环上的点的连接方式,求一下相邻两

BZOJ 2878([Noi2012]-失落的游乐园树DP+出站年轮加+后市展望DP+vector的erase)

2878: [Noi2012]迷失乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z认为呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现能够将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m仅仅可能等于n或者n-1). 小Z如今所在的大门也正好

NOI2012 迷失游乐园

http://www.lydsy.com/JudgeOnline/problem.php?id=2878 比较容易的概率题. Case1~5: 这是一棵树. 我们求出每个点i度数du[i],只走子树的期望距离g[i]和不走子树的期望距离f[i],这比较好求. 然后累加即可. Case6~10: 图中有一个环,然后环上的点都是一棵树的根. 对于每棵树,我们同样求出每个点i度数du[i],只走子树的期望距离g[i]. 那么怎么求不走子树的期望距离f[i]呢? 我们先求环上的点的f[i]吧. 我们枚举

bzoj2878 [Noi2012]迷失游乐园 [树形dp]

Description 放假了,小Z认为呆在家里特别无聊.于是决定一个人去游乐园玩. 进入游乐园后.小Z看了看游乐园的地图,发现能够将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m仅仅可能等于n或者n-1).小Z如今所在的大门也正好是一个景点. 小Z不知道什么好玩,于是他决定,从当前位置出发,每次随机去一个和当前景点有道路相连的景点,而且同一个景点不去两次(包括起始景点).贪玩的小Z会一直游玩.直到当前景点的相邻景点都已经訪问过为止.小Z全部经过的景点按顺序构成一条非反

[bzoj2878][Noi2012]迷失游乐园(基环树dp)

bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个$dp$数组分别记录从这个点起向上向下走的期望 向下走的$dp$不用多说 向上走的$dp$: 对于从$u$计算$v$的dp $dp[v]$应当是从u向周围引出所有路径减去走向t的路径的期望后再除以$deg_{u}-1$ 对于基环树: 环上的点很少. 此时环上的点的向上$dp$指从u出发向环上两头走的期望. 如何计算:对于环上每一个点都向环的两头各dp一次取平均值

【BZOJ】【2878】【NOI2012】迷失游乐园

树形+基环树DP/数学期望 然而我并不会做…… 题解戳这里:http://blog.csdn.net/u011265346/article/details/46328543 好吧先考虑一个简单点的,当m=n-1时,整个是一个树形的结构,无根树我们一般还是转成有根树来处理……然后既然是无法回头的,那么我们可以定一下方向:向下或者向上(废话) 定义一下: son[x]为x的儿子的数量 down[x]表示从x这个点出发,向叶子们走的期望长度. 怎么算呢?其实就是所有可能的情况(所有的儿子)加起来,再求