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

Solution

up[x]表示从节点x向上走的期望长度,down[x]表示从节点x向下走的期望长度。

假设是一棵树,两遍dfs搞定。

假设有一个环:

以环上每一个点为向下根dfs出down[x]。

但此时对于环上的点的down[x]不是向下走的期望长度,还要枚举环上的点,计算沿环走的期望长度。

最后计算非环上的点的up值。

话说12年的题有点丧病啊。

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 100005;

struct Edge{
int to;
double v;
Edge() {}
Edge(int a, double b) : to(a), v(b) {}
}; vector<Edge> edges[MAXN];
int n, m;
int vis[MAXN], flag;
double son[MAXN], fa[MAXN], up[MAXN], down[MAXN];
int cir[MAXN], tot, hash[MAXN];
int pre[MAXN], nex[MAXN];
double len[25][25]; void findCircle(int x, int f) {
vis[x] = 1;
for (int i = 0; i < edges[x].size(); i++) {
Edge e = edges[x][i];
if (e.to == f) continue;
if (vis[e.to]) {
flag = e.to;
return;
}
findCircle(e.to, x);
if (flag > 0) {
if (flag == x) flag = -1;
return;
}
if (flag == -1) break;
}
vis[x] = 0;
} void dfs_cir(int x, int f) {
if (hash[x]) return;
cir[++tot] = x;
hash[x] = tot;
fa[x] = 2;
for (int i = 0; i < edges[x].size(); i++) {
Edge e = edges[x][i];
if (e.to == f) continue;
if (!vis[e.to]) continue; pre[e.to] = x;
nex[x] = e.to;
dfs_cir(e.to, x);
len[hash[x]][hash[e.to]] = len[hash[e.to]][hash[x]] = e.v;
break;
}
} void dfsdown(int x, int f) {
for (int i = 0; i < edges[x].size(); i++) {
Edge e = edges[x][i];
if (!vis[e.to] && e.to != f) {
fa[e.to] = 1;
dfsdown(e.to, x);
son[x]++;
down[x] += down[e.to] + e.v;
}
}
if (son[x]) down[x] /= son[x];
} void dfsup(int x, int f, double ee) {
up[x] = ee;
if (fa[f] + son[f] > 1) up[x] += (fa[f] * up[f] + son[f] * down[f] - down[x] - ee) / (fa[f] + son[f] - 1);
for (int i = 0; i < edges[x].size(); i++)
if (edges[x][i].to != f) dfsup(edges[x][i].to, x, edges[x][i].v);
} int main() {
scanf("%d %d", &n, &m);
int a, b;
double c;
for (int i = 0; i < m; i++) {
scanf("%d %d %lf", &a, &b, &c);
edges[a].push_back(Edge(b, c));
edges[b].push_back(Edge(a, c));
}
findCircle(1, 0);
if (m < n) {
dfsdown(1, 0);
for (int i = 0; i < edges[1].size(); i++)
dfsup(edges[1][i].to, 1, edges[1][i].v);
} else {
for (int i = 1; i <= n; i++) {
if (vis[i]) {
dfs_cir(i, 0);
break;
}
}
for (int i = 1; i <= tot; i++)
dfsdown(cir[i], 0);
for (int i = 1; i <= tot; i++) {
int u = cir[i];
double k = 1;
for (int j = nex[u]; j != u; j = nex[j]) {
if (nex[j] != u) up[u] += k * (len[hash[pre[j]]][hash[j]] + down[j] * son[j] / (son[j] + 1));
else up[u] += k * (len[hash[pre[j]]][hash[j]] + down[j]);
k /= (son[j] + 1);
}
k = 1;
for (int j = pre[u]; j != u; j = pre[j]) {
double z = up[j];
if (pre[j] != u) up[u] += k * (len[hash[nex[j]]][hash[j]] + down[j] * son[j] / (son[j] + 1));
else up[u] += k * (len[hash[nex[j]]][hash[j]] + down[j]);
k /= (son[j] + 1);
}
up[u] /= 2;
}
for (int i = 1; i <= tot; i++) {
for (int j = 0; j < edges[cir[i]].size(); j++) {
Edge e = edges[cir[i]][j];
if (!hash[e.to]) dfsup(e.to, cir[i], e.v);
}
}
} double ans = 0;
for (int i = 1; i <= n; i++) {
ans += (up[i] * fa[i] + down[i] * son[i]) / (fa[i] + son[i]);
}
printf("%.5lf\n", ans / n);
return 0;
}

bzoj2878 [Noi2012]迷失游乐园 [树形dp]的更多相关文章

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

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

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

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

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

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

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

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

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

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

  6. [luogu2081 NOI2012] 迷失游乐园 (树形期望dp 基环树)

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

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

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

  8. BZOJ2878 [Noi2012]迷失游乐园

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  9. Luogu P2081 [NOI2012]迷失游乐园 | 期望 DP 基环树

    题目链接 基环树套路题.(然而各种错误调了好久233) 当$m=n-1$时,原图是一棵树. 先以任意点为根做$dp$,求出从每一个点出发,然后只往自己子树里走时路径的期望长度. 接着再把整棵树再扫一遍 ...

随机推荐

  1. android 提纲挈领

    之后的android学习将侧重三方面: 1.基础内容例如xml属性.sharedpreference.数据库必须能够熟记于心. 2.开源library熟练应用,能够了解如何更好地使用各种开源libra ...

  2. 基于artDialog的扩展

    /* * * 引用此文件必须引用以下两个资源文件,并且还要引用jQuery * <link href="ui-dialog.css" rel="stylesheet ...

  3. 7.union

    联合结果集union 简单的结果集联合: select number,name,age from emp union select cardnumber,name,age from emp2 基本的原 ...

  4. Android Fragment间的广播消息接收

    这种方式不用在配置文件加东西,我比较喜欢. 广播注册,可以写在Activity(onCreate),也可以写在Fragment(onActivityCreated)里. LocalBroadcastM ...

  5. ThreadPoolExecutor理解

    ThreadPoolExecutor组成 ThreadPoolExecutor的核心构造函数: public ThreadPoolExecutor(int corePoolSize, int maxi ...

  6. 【SQL】CASE与DECODE

    1. case..when case..when语句用于按照条件返回查询结果,如当我们想把emp表的工资按照多少分成几个不同的级别,并分别统计各个级别的员工数.SQL语句如下: select (cas ...

  7. oracle从入门到精通复习笔记续集之PL/SQL(轻量版)

    复习内容: PL/SQL的基本语法.记录类型.流程控制.游标的使用. 异常处理机制.存储函数/存储过程.触发器. 为方便大家跟着我的笔记练习,为此提供数据库表文件给大家下载:点我下载 为了要有输出的结 ...

  8. AI:AI是什么?

    古老的哲学对科学有永远的借鉴意义,科学上的咬文嚼字往往会让其丧失完备性. 一.AI是什么 你看起来它有多好,它就有多好.本质只能通过表象来描述,在色即是空的逻辑里,图灵测试也许是最精准的AI测试方式. ...

  9. Kind (type theory)-higher-kinded types--type constructor

    , pronounced "type", is the kind of all data types seen as nullary type constructors, and ...

  10. js判断数组中是否包含某个值

    /** * 判断数组中是否包含某个值 * @param arr 数组 * @param str 值 * @returns {boolean} */ function contains(arr, str ...