NOI 2012 【迷失游乐园】
这道题,额,反正我是刚了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 【迷失游乐园】的更多相关文章
- BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )
一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. -------- ...
- BZOJ 2878 【NOI2012】 迷失游乐园
题目链接:迷失游乐园 这道题也没有传说中的那么难写吗→_→ 似乎有篇博客讲得特详细……附上链接:戳这里 如果这道题不是基环树,而就是一棵树的话,我们来考虑改怎么做.因为树上的路径只有向上.向下两种走法 ...
- 【BZOJ】【2878】【NOI2012】迷失游乐园
树形+基环树DP/数学期望 然而我并不会做…… 题解戳这里:http://blog.csdn.net/u011265346/article/details/46328543 好吧先考虑一个简单点的,当 ...
- 【BZOJ2878】【NOI2012】迷失游乐园(动态规划)
[BZOJ2878][NOI2012]迷失游乐园(动态规划) 题面 BZOJ 题解 记得以前考试的时候做过这道题目 这题的暴力还是非常显然的,每次\(dfs\)一下就好了. 时间复杂度\(O(n^2) ...
- 【BZOJ 2878】 2878: [Noi2012]迷失游乐园 (环套树、树形概率DP)
2878: [Noi2012]迷失游乐园 Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m ...
- 【NOI2012】迷失游乐园
题目链接:迷失游乐园(BZOJ) 迷失游乐园(Luogu) 独立完成的题,写一发题解纪念一波~ 模拟完样例大概可以知道是道树形DP了. 观察数据范围,发现是基环树,至少会有一个环. 先从树的部分开始 ...
- 「NOI2012」迷失游乐园
「NOI2012」迷失游乐园 题目描述 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩. 进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该 ...
- [bzoj2878][Noi2012]迷失游乐园(基环树dp)
[bzoj2878][Noi2012]迷失游乐园(基环树dp) bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个 ...
- 2878: [Noi2012]迷失游乐园 - BZOJ
Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环( ...
随机推荐
- 遇到doxygen生成的chm文档目录如果有中文是乱码?
原因不在于doxygen,它没有问题,问题出在微软的HTML Help Workshop的hhc.exe不支持utf8.所以要解决这个问题,需要做两个额外的步骤: 1.将html/index.hhp中 ...
- C# 获取当前月的月初和月末
/// <summary> /// 获取当前月的月末日期 /// </summary> /// <returns></returns> public s ...
- Mybatis源码学习第七天(插件源码分析)
为了不把开发和源码分析混淆,决定分开写; 接下来分析一下插件的源码,说道这里老套路先说一个设计模式,他就是责任链模式 责任链模式:就是把一件工作分别经过链上的各个节点,让这些节点依次处理这个工作,和装 ...
- RocketMQ生产部署架构如何设计
前言 看了我们之前的文章,相信小伙伴们对RocketMQ已经有了一个初步的了解,那么今天我们就来聊一聊具体如何来设计一套高可用的生产部署架构. 在聊如何设计这套架构的同时,我们再补充一些之前没提到的知 ...
- Web开发初探(系统理解Web知识点)
一.Web开发介绍 我们看到的网页通过代码来实现的 ,这些代码由浏览器解释并渲染成你看到的丰富多彩的页面效果. 这个浏览器就相当于Python的解释器,专门负责解释和执行(渲染)网页代码. 写网页的代 ...
- oracle之二控制文件
控制文件 3.1 控制文件的功能和特点: 1)定义数据库当前物理状态,不断在往controlfile写入[SCN等] 2)维护数据的一致性 3)是一个二进制文件 ...
- 【二叉树-所有路经系列(根->叶子)】二叉树的所有路径、路径总和 II、路径总和、求根到叶子节点数字之和(DFS)
总述 全部用DFS来做 重点一:参数的设置:为Root,路径字符串,路径List集合. 重点二:步骤: 1 节点为null 2 所有节点的操作 3 叶子结点的操作 4 非叶节点的操作 题目257. 二 ...
- SpringCloud实战 | 第四篇:SpringCloud整合Gateway实现API网关
一. 前言 微服务实战系列是基于开源微服务项目 有来商城youlai-mall 版本升级为背景来开展的,本篇则是讲述API网关使用Gateway替代Zuul,有兴趣的朋友可以进去给个star,非常感谢 ...
- hystrix(5) 延时检测
hystrix在执行命令的同事,会对执行延时进行检测,如果超过设置的延时时间,那么将停止执行,并返回fallback执行结果. if (properties.executionTimeoutEnabl ...
- pytest自学第一期
开始自学pytest了,我并不想看网上的各种自学教程和文档,要看咱们今天就看pytest的官方文档,不会英语咱们就用翻译,看不懂原理咱们就翻源码,就人肉试错 学习一个技术,使用速成鸡的套路是一个办法, ...