这道题,额,反正我是刚了2天,然后就萎了。。。。。。(是不是觉得我很菜)

题目描述:

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

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

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

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

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

数据范围:n<=100000,环中节点个数小于20,。

思路分析:

这题,怎么说呢?额,很恶心,嗯,没错,就是这样。

前两天,我没看题解,自己做,想了1个钟头思路就想出来了,但是代码调了两天还是错,第三天一看题解,发现需要注意的细节实在太多了,恶心的一批,调得我想杀人。。。

先将一下大体思路吧。

环中节点数小于20,很明显了,就是写一个基环树DP嘛,既然这样的话,我们就先来说一下一颗树的怎么写吧。

对于树上的一个节点,以它为出发点显然要分两种情况讨论:

1、往它的子树走(记作down操作)

2、往它的父亲走(记作up操作)

很明显可以用换根DP的方式来求解。状态:f[u]表示以u为起点,往u的子树走的期望长度,代码中的期望是没有算当前u节点影响的,其实你们也可以挑战一下直接表示期望的状态,反正我是挑战失败了,分类讨论写了一大堆,愣是没调出来。。。。。。先给大家说一说我为什么直接算期望难写吧,随便给大家举几个例子:

1、算f[u]要除以du[u],而路径上的其他点都只要除以du[u]-1就够了(一条边是由父亲走过来的,所以不用算)。

2、du[u]-1=0,u是否在环上,在的话du[u]要-2。

3、做环的时候,出度要在du[u]-2,du[u]-1,du[u]之间来回变化,还要根据是否有子树来判断是否应该停止。

4、......

然后,我差点把编译器卸了。。。。。。

好,接着我们刚才的状态,转移很简单:

f[u]=[Σv是u的儿子 (f[v]+dist[i])]/(du[u]-1)          若u为根则不用-1。(这个转移是直接表示期望)

d[u]=Σv是u的儿子 (f[v]+dist[i])                                                         (这是没算u的,代码里d用了p代替,但最终的dp数组还是d)

那么以v为起点往上走应该怎么做呢?

考虑换根,我们先把以u(v的父亲)为起点的期望路径长度减去儿子v对u的贡献,然后把u剩下的部分当做v的一个子树,算出u对于v的贡献。

转移:d[v]=(d[u]-f[v]-dist[i])/max(1,du[u]-1)+dist[i]

重点来了,注意方程中的du[u]-1,这是我再第二天才发现并想通的,当时满怀希望地交了一发,结果成功的得到了50分的好成绩,拿到了“再写N个讨论还是WA”的奖励。。。

为什么是du[u]-1呢,不是以v为起点吗,那不应该是du[v]吗,怎么会是du[u]-1呢?

想想我们对于dp的定义,看看我们是从哪里转移过来的,定义告诉我们,u对于d[u]的影响我们是还没有算的,而v对于d[v]的影响我们是不用算的(会在最后统计答案时统一除去),那么很清楚这个我们要算的是u对于d[v]的影响,而不是v对于d[v]的影响,所以便是du[u],而后面的那个-1就是因为u是从v走过来的,所以要除去u与v之间的连边。

在一颗树上的思路就是这样啦,很恶心不是吗?

那么恭喜你,当你处理完这么细节后,基环树上的dp过程中还有几个恶心的细节等着你去发现与处理。

先讲一下基环树dp的大体思路吧,对于环上的每一个点,都先对其对应子树跑一遍down操作。然后根据这些,算出每一个环上的点以它为起点的期望路径长度(也就是d[i])。再对环上的每一个点,对其对应子树跑一遍up操作。

思路很简单,——开始处理细节吧,少年!!!

我们剩下要处理的便是如何根据这些算出每一个环上的点以它为起点的期望路径长度了。

以u为起点,遍历环中节点。

若点v是已经转完了大半圈,下一个访问的节点就是u了,那么du[v]就要-2,因为v之前的一个点不能走,下一个点u也不能走。若v不属于刚才那种情况,那么du[v]就-1,因为之前的那个点不能走。g[u]=[∑v与u相连,且未被访问  (g[v]+dist[i])]/(du[u]-1或2)   注意如果节点v属于第1种情况则不能加dist[i]。

呼——,终于写完了!

写这篇题解的时候其实我一直都忘不了一开始被恶心细节统治的恐惧。。。。。。

代码实现:

var
f,d,g,p:array[1..100000]of double;
vis,cir:array[1..100000]of boolean;
next,dist,vet:array[1..200000]of longint;
head,du,fa:array[1..100000]of longint;
i,n,m,x,y,z,tot,root:longint;
ans:double;
function max(a,b:longint):longint;
begin
if a>b then exit(a) else exit(b);
end;
procedure add(x,y,z:longint);
begin
inc(tot);
next[tot]:=head[x];
vet[tot]:=y;
head[x]:=tot;
dist[tot]:=z;
end;
procedure circle(u,father:longint);
var
i,v:longint;
begin
i:=head[u]; vis[u]:=true;
while i<>0 do
begin
v:=vet[i];
if (fa[u]<>v)and(vis[v]) then
begin
x:=u; fa[v]:=u;
while not cir[x] do
begin cir[x]:=true; x:=fa[x]; end;
end;
if not vis[v] then
begin fa[v]:=u; circle(v,u); end;
i:=next[i];
end;
end;
procedure down(u:longint);
var
i,v:longint;
begin
i:=head[u]; vis[u]:=true;
while i<>0 do
begin
v:=vet[i];
if (not vis[v])and(not cir[v]) then
begin
down(v);
p[u]:=p[u]+f[v]+dist[i];
inc(du[u]);
end;
i:=next[i];
end;
if du[u]>0 then f[u]:=p[u]/du[u];
if u<>root then inc(du[u]);
end;
procedure up(u,father:longint);
var
i,v:longint;
begin
i:=head[u];
while i<>0 do
begin
v:=vet[i];
if (v<>father)and(not cir[v]) then
begin
d[v]:=d[v]+(d[u]-f[v]-dist[i])/max(1,du[u]-1)+dist[i]; //du[u]-1要吃透
up(v,u);
end;
i:=next[i];
end;
end;
procedure dfs(u,father:longint);
var
i,v,k:longint;
flag:boolean;
begin
i:=head[u]; flag:=false; vis[u]:=true; d[u]:=0;
while i<>0 do
begin
v:=vet[i];
if (v<>father)and(cir[v])and(v<>root) then
begin
flag:=true; dfs(v,u);
d[u]:=d[u]+d[v]+dist[i];
end;
i:=next[i];
end;
if u=root then exit; //恶心细节处理
k:=max(1,du[u]);
if flag then begin k:=du[u]+1; d[u]:=(d[u]+p[u])/k; end
else d[u]:=f[u];
end;
begin
read(n,m);
for i:=1 to m do
begin
read(x,y,z);
add(x,y,z); add(y,x,z);
end;
if n=m then
begin
circle(1,0); //找环
fillchar(vis,sizeof(vis),false);
for i:=1 to n do
if cir[i] then begin root:=i; down(i); end;
for i:=1 to n do
if cir[i] then
begin root:=i; dfs(i,0); g[i]:=d[i]; end;
for i:=1 to n do d[i]:=p[i];
for i:=1 to n do
if cir[i] then begin d[i]:=d[i]+g[i]; du[i]:=du[i]+2; end;
for i:=1 to n do
if cir[i] then up(i,0);
for i:=1 to n do
ans:=ans+d[i]/du[i];
writeln(ans/n:0:2);
end else
begin
root:=1;
down(1);
for i:=1 to n do
d[i]:=p[i];
up(1,0);
for i:=1 to n do
ans:=ans+d[i]/du[i];
writeln(ans/n:0:2);
end;
end.

NOI 2012 【迷失游乐园】的更多相关文章

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

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

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

    题目链接:迷失游乐园 这道题也没有传说中的那么难写吗→_→ 似乎有篇博客讲得特详细……附上链接:戳这里 如果这道题不是基环树,而就是一棵树的话,我们来考虑改怎么做.因为树上的路径只有向上.向下两种走法 ...

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

    树形+基环树DP/数学期望 然而我并不会做…… 题解戳这里:http://blog.csdn.net/u011265346/article/details/46328543 好吧先考虑一个简单点的,当 ...

  4. 【BZOJ2878】【NOI2012】迷失游乐园(动态规划)

    [BZOJ2878][NOI2012]迷失游乐园(动态规划) 题面 BZOJ 题解 记得以前考试的时候做过这道题目 这题的暴力还是非常显然的,每次\(dfs\)一下就好了. 时间复杂度\(O(n^2) ...

  5. 【BZOJ 2878】 2878: [Noi2012]迷失游乐园 (环套树、树形概率DP)

    2878: [Noi2012]迷失游乐园 Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m ...

  6. 【NOI2012】迷失游乐园

    题目链接:迷失游乐园(BZOJ)  迷失游乐园(Luogu) 独立完成的题,写一发题解纪念一波~ 模拟完样例大概可以知道是道树形DP了. 观察数据范围,发现是基环树,至少会有一个环. 先从树的部分开始 ...

  7. 「NOI2012」迷失游乐园

    「NOI2012」迷失游乐园 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该 ...

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

    [bzoj2878][Noi2012]迷失游乐园(基环树dp) bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个 ...

  9. 2878: [Noi2012]迷失游乐园 - BZOJ

    Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环( ...

随机推荐

  1. App性能测试前需要了解的内存原理

    这两天在研究性能中内存方面的一块,网上也零散看了挺多文章,写得很细但是感觉不够整体,所以这篇算是总结一下吧,当个复习资料. 那么这里个人分为两个大部分,第一部分应用内的内存管理,主要是oom的理解,G ...

  2. 汽车芯片如何高效符合ISO 26262功能安全标准

      汽车芯片和集成电路(IC)是高级驾驶员辅助系统(advanced driver assistance systems-ADAS)和联网自动驾驶汽车(connected autonomous veh ...

  3. Explain Plan试分析

    注:以下是本人对Explain Plan的试分析,有不对的地方希望大家指出.关于如何查看Oracle的解释计划请参考:https://www.cnblogs.com/xiandedanteng/p/1 ...

  4. Promise核心实现

    核心 构造函数核心 维护状态变量,只能由pending变为resolve或者reject 维护一个存储结果的变量 维护一个回调数组,执行到then,如果我们传入的立即执行函数没有立即执行resolve ...

  5. [LeetCode]695. 岛屿的最大面积(DFS/BFS)、200. 岛屿数量(DFS/BFS待做/并差集待做)

    695. 岛屿的最大面积 题目 给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合.你可以假设二维矩阵的四个边缘都被 ...

  6. [LeetCode]33. 搜索旋转排序数组(二分)

    题目 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个目 ...

  7. k8s集群监控(十一)

    Weave Scope   在我之前的docker监控中<Docker容器监控(十)–技术流ken>就已经提到了weave scope. Weave Scope 是 Docker 和 Ku ...

  8. 云计算openstack共享组件——消息队列rabbitmq(3)

    一.MQ 全称为 Message Queue, 消息队列( MQ ) 是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们. 消息传 ...

  9. 217。数据中是否有重复元素(哈希表/set简法)

    给定一个整数数组,判断是否存在重复元素. 如果任意一值在数组中出现至少两次,函数返回 true .如果数组中每个元素都不相同,则返回 false . 示例 1: 输入: [1,2,3,1] 输出: t ...

  10. 使用Scrcpy实现电脑控制安卓手机

    很多时候我们想要在电脑上使用一些手机软件,使用模拟器当然是一种选择,但是这些模拟器要不然不免费,要不然广告多不放心.Scrcpy是一个开源免费的软件,通过abd命令实现了安卓手机投屏和控制功能,并且支 ...