本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

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.00000

【样例解释】样例数据中共有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

正解:基环外向树上DP

解题报告:

  $m=n-1$、$m=n$分别是树和基环外向树,显然基环外向树就是在树的情况下多了一个环,比树再多考虑一些环上的移动即可...

  树的情况我们考虑对于以每个点为起点时的情况,我们先随便找出一个点作为根节点,则每个点在树上要么往上走,要么往下走,走的期望长度分别设为$up[x]$和$down[x]$。

  先考虑往下走的情况,我们显然需要先知道每个儿子节点的$down$。

  那么$down[x]$就是所有儿子节点的$down$$+$儿子走到根这条边的距离的平均值,一遍$dfs$即可解决。

  $up[x]$有一点复杂,假设往上走,走到了父亲节点,那么又有两种方案,要么走进另外的儿子节点,要么继续往上走。

  所以就是走到儿子节点的期望长度与往上走的期望长度取平均值。

  树的情况比较simple。

  考虑基环外向树的情况,因为环的大小不会超过$20$,对于环上的每个节点,我可以先算出由这个节点拓展出去的树的情况,这与之前的树是一样的做法。

  但是$up$值计算不同:环上的一个点,可以往两个方向走,而概率的计算我们需要仔细想一想。令

  $sonnum[x]$为$x$的儿子节点数目,那么往子树走的贡献就可以直接算出来,继续在环上走的概率就为$\frac{1}{sonnum[x]}$。

  按照这样的计算方法就可以算出环上的$up$值。

  最后再$dfs$一遍,就可以算出每棵子树的$up$值。

  统计最终答案的方法就是考虑一个点是往上还是往下,这里不赘述。我的代码似乎写复杂了一点,不过思想还是很好理解的。

  细节很多,细心调试...(祝身体健康)

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 100011;
const int MAXM = 200011;
int n,m,ecnt,first[MAXN],to[MAXM],next[MAXM],w[MAXM];
int son_num[MAXN],w_fa[MAXN],havfa[MAXN];
double down[MAXN],up[MAXN],ans; inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} namespace Tree{
inline void dfs1(int x,int fa){
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue; havfa[v]=1;
son_num[x]++; w_fa[v]=w[i]; dfs1(v,x);
down[x]+=down[v]+w[i];
}
if(son_num[x]>0) down[x]/=(double)son_num[x];
} inline void dfs2(int x,int fa){
if(fa!=0) {
up[x]=down[fa]*son_num[fa]-down[x]-w_fa[x]+up[fa];//从当前节点走上父亲再走下所有儿子节点,再考虑从父亲往上走的情况
up[x]/=(double)(son_num[fa]+havfa[fa]-1); up[x]+=w_fa[x];//必须要判断父亲有没有父亲!否则不能往上走!
}
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs2(v,x);
}
} inline void work(){
dfs1(1,0);
dfs2(1,0);
}
} namespace Circle_and_tree{
bool iscircle[MAXN],vis[MAXN];
int pre[MAXN],father[MAXN],dui[45],cir_cnt,W[45][45];
inline void dfs_circle(int x,int fa){//找环
vis[x]=1;
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
if(vis[v]) {
if(iscircle[v]) continue;
int u=x;
while(1) {
iscircle[u]=true; dui[++cir_cnt]=u;
pre[u]=father[u];
if(cir_cnt>1) W[cir_cnt][cir_cnt-1]=W[cir_cnt-1][cir_cnt]=w_fa[dui[cir_cnt-1]];
if(u==v) break;
u=father[u];
}
W[cir_cnt][1]=W[1][cir_cnt]=w[i];
continue;
}
father[v]=x; w_fa[v]=w[i];
dfs_circle(v,x);
}
} inline void dfs1(int x,int fa){//得到环上每个点向树中走的down值
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(iscircle[v] || v==fa) continue;
dfs1(v,x);
son_num[x]++; father[v]=x; w_fa[v]=w[i]; havfa[v]=1;
down[x]+=down[v]+w[i];
}
if(son_num[x]) down[x]/=(double)son_num[x];
} inline void dfs2(int x,int fa){//得到环上每个点向树中走的up值
if(fa!=0) {//环上的点不用再算up值了
up[x]=down[fa]*son_num[fa]-down[x]-w_fa[x]+up[fa]*havfa[fa];
//同时考虑父亲节点可能是根节点,可以往环上两边走
//从当前节点走上父亲再走下所有儿子节点,再考虑从父亲往上走的情况
up[x]/=(double)(son_num[fa]+havfa[fa]-1); up[x]+=w_fa[x];//必须要判断父亲有没有父亲!否则不能往上走!
}
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa || iscircle[v]) continue;
dfs2(v,x);
}
} inline void work(){
dfs_circle(1,0); int u,now,last,nex; double nowp;
memset(father,0,sizeof(father));
memset(w_fa,0,sizeof(w_fa));
for(int i=1;i<=cir_cnt;i++) dfs1(dui[i],0);
for(int i=1;i<=cir_cnt;i++) havfa[dui[i]]=2; //两个方向
for(int i=1;i<=cir_cnt;i++) {
nowp=1.0; u=i+1; last=i; if(u>cir_cnt) u=1;
while(u!=i) {//向编号变大方向
now=dui[u];//从i走到环上的当前点
//不要漏掉了环上点之间的长度!!!
if((u%cir_cnt+1)==i) up[dui[i]]+=(double)nowp*(down[now]+W[last][u]);//特判最后一条环边不能走!
else up[dui[i]]+=(double)nowp*(son_num[now]*down[now]/(son_num[now]+1)+W[last][u]);
nowp/=(double)(son_num[now]+1); last=u;
u++; if(u>cir_cnt) u=1;
} nowp=1.0; u=i-1; last=i; if(u==0) u=cir_cnt;
while(u!=i) {//向编号变小方向
now=dui[u];//从i走到环上的当前点
nex=u; nex--; if(nex==0) nex=cir_cnt;
if(nex==i) up[dui[i]]+=(double)nowp*(down[now]+W[last][u]);//特判最后一条环边不能走!
else up[dui[i]]+=(double)nowp*(son_num[now]*down[now]/(son_num[now]+1)+W[last][u]);
nowp/=(double)(son_num[now]+1); last=u;
u=nex;
}
up[dui[i]]/=2.0;//取平均值
}
for(int i=1;i<=cir_cnt;i++) dfs2(dui[i],0);
}
} inline void work(){
n=getint(); m=getint(); int x,y,z;
for(int i=1;i<=m;i++) {
x=getint(); y=getint(); z=getint();
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
}
if(m==n-1) Tree::work();
else Circle_and_tree::work(); for(int i=1;i<=n;i++)
ans+=(double)(down[i]*son_num[i]+up[i]*havfa[i])/(son_num[i]+havfa[i]);
ans/=(double)n;//平均值
printf("%.5lf",ans);
} int main()
{
work();
return 0;
}

  

BZOJ2878 [Noi2012]迷失游乐园的更多相关文章

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

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

  2. BZOJ2878 NOI2012迷失游乐园(树形dp+环套树+概率期望)

    考虑树的部分分怎么做.令f[i]为i向子树内走的期望路径长度,转移比较显然.算答案时先把其父亲的答案弄好就可以统计自己的答案了. 环套树也类似.树里直接dp,对环上点暴力考虑环上的每条路径,算完后再在 ...

  3. BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】

    题目链接 BZOJ2878 题解 除了实现起来比较长,思维难度还是挺小的 观察数据范围发现环长不超过\(20\),而我们去掉环上任何一个点就可以形成森林 于是乎我们枚举断掉的点,然后只需求出剩余每个点 ...

  4. [BZOJ2878][NOI2012]迷失游乐园(环套树DP+概率)

    推荐讲解:https://www.cnblogs.com/Tunix/p/4561493.html 首先考虑树的情况,就是经典的树上概率DP.先DP出down表示从这个点向儿子走能走的期望长度,再DP ...

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

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

  6. bzoj2878 [Noi2012]迷失游乐园——概率期望DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2878 这个博客写得很好:https://www.cnblogs.com/qt666/p/72 ...

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

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

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

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

  9. 【bzoj2878】 Noi2012—迷失游乐园

    http://www.lydsy.com/JudgeOnline/problem.php?id=2878 (题目链接) 题意 求基环树上以任意点为起点的简单路径期望长度. Solution 啊啊啊好丑 ...

随机推荐

  1. Markov chain

    w https://en.wikipedia.org/wiki/Markov_chain https://zh.wikipedia.org/wiki/马尔科夫链 In probability theo ...

  2. Maven国内高速镜像

    大家都知道国外Maven中央仓库下载jar 没谁了速度, 给大家分享个国内666的Maven镜像 阿里云Maven镜像  希望能帮到大家: 配置下setting.xml即可: <mirrors& ...

  3. 介绍一下Python中webbrowser的用法?

    webbrowser模块提供了一个高级接口来显示基于Web的文档,大部分情况下只需要简单的调用open()方法.webbrowser定义了如下的异常:exception webbrowser.Erro ...

  4. 进程、数据共享、进程锁、进程池、requests模块和bs4(beautifulsoup)模块

    一.进程 1.进程间数据不共享,如下示例: import multiprocessing data_list = [] def task(arg): data_list.append(arg) pri ...

  5. CSDN论坛停站维护公告

    各位亲爱的用户:         CSDN论坛将于2014年11月9日23点至11月10日8点进行积分系统的升级,升级期间会影响大家的正常訪问和操作.给大家带来不便,敬请广大用户谅解.         ...

  6. Insert Buffering

    14.5.13.4 Insert Buffering Database applications often insert new rows in the ascending order of the ...

  7. SpringMVC 课纲

    SpringMVC 课纲 第一章 SpringMVC 架构 一个简单的 web 项目,校验器 SpringMVC 组件及相互关系 第二章 数据绑定 form标签库 第三章 Converter 和 Fo ...

  8. Python数据分析与挖掘所需的Pandas常用知识

    Python数据分析与挖掘所需的Pandas常用知识 前言Pandas基于两种数据类型:series与dataframe.一个series是一个一维的数据类型,其中每一个元素都有一个标签.series ...

  9. 判断IP地址是否合法

    /* return 1 if string contain only digits, else return 0 */ int valid_digit(char *ip_str) { while (* ...

  10. python 使用pip install 手动安装本地包的方法

    Installing pystan manually fixed the issue (otherwise it would just hang forever). ~/GitHub $ git cl ...