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条道路的无向连通图,且该图中至多有一个环( ...
随机推荐
- 初级知识点二——C#值传递
C#中有值传递和引用传递,这个东西一直有点儿绕,今天花点儿时间来把这个事情搞清楚. 传递值类型的参数 值类型的变量,是直接包含其数据的.实际上,在向方法传递一个值类型变量,其实就意味着向方法传递了一个 ...
- List集合对象去重及按属性去重的8种方法-java基础总结系列第六篇
最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大.基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华.所以我更想把java ...
- spring boot+spring security集成以及Druid数据库连接池的问题
贴工程目录,其中bll目录下是service+dao层,common是一些公用的模块及功能类,web是controller层 用到了druid及Redis,工具及配置类目录(本文不介绍如何配置drui ...
- Fragment时长统计那些事
注:本文同步发布于微信公众号:stringwu的互联网杂谈 frament时长统计那些事 页面停留时长作为应用统计的北极星指标里的重要指标之一,统计用户在某个页面的停留时长则变得很重要.而Fragme ...
- 关于Java代理那些事儿
代理是啥?就是代替你执行你持有的主要功能,并且能在你的基础之上完成一些其他的功能.代理的目的就是生成一个和原对象具有同样功能的对象.在Java中,代理是一种设计模式.在Spring的面向切面编程(AO ...
- Web开发初探(系统理解Web知识点)
一.Web开发介绍 我们看到的网页通过代码来实现的 ,这些代码由浏览器解释并渲染成你看到的丰富多彩的页面效果. 这个浏览器就相当于Python的解释器,专门负责解释和执行(渲染)网页代码. 写网页的代 ...
- C++ Templates (2.3 类模板的局部使用 Partial Usage of Class Templates)
返回完整目录 目录 2.3 类模板的局部使用 Partial Usage of Class Templates 2.3.1 Concepts 2.3 类模板的局部使用 Partial Usage of ...
- [程序员代码面试指南]数组和矩阵-数组的partition调整
题目 补充问题:数组只含0,1,2,对数组排序,要求时间复杂度O(n),额外空间复杂度O(1) 题解 维护三个变量,l,idx,r.左区间[0,l],中间区间[l+1,idx],右区间[idx+1,r ...
- Python爬虫开发者工具介绍
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. chrome 开发者工具 当我们爬取不同的网站时,每个网站页面的实现方式各不相同,我们需要对 ...
- 【5】JMicro免费在线消息服务
JMicro是一个用Java语言实现的开源微服务全家桶, 源码地址:https://github.com/mynewworldyyl/jmicro, Demo地址:http://jmicro.cn/. ...