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

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;
}
时间: 2024-10-10 05:24:40

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

BZOJ 2878([Noi2012]迷失游乐园-树形DP+环加外向树+期望DP+vector的erase)

2878: [Noi2012]迷失游乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1).小Z现在所在的大门也正好是

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

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

BZOJ 2878 [Noi2012]迷失游乐园 树形期望DP+基环树

题意:链接 方法:树形期望DP+基环树 解析: 首先先看前50%的数据 是一棵树 那么我们可以搞树形DP 然后设几个正常的数组 sum[i]代表i走i的子节点的期望的和. down[i]代表从底下走到i的期望. size[i]代表i的儿子个数 up[i]代表从i往上走的期望 然后就可以推式子了 显而易见地可以推出来up的式子 然后有一些奇怪的关于根节点的特判,注意一下就OK了. 然后后50% 我们发现它是一个基环树? 那么首先可以乱搞出来环上的点,然后记录一下这个环上的点的连接方式,求一下相邻两

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

bzoj luogu 题意:一颗数或是基环树,随机从某个点开始一直走,不走已经到过的点,求无路可走时的路径长期望. 对于一棵树: 用两个$dp$数组分别记录从这个点起向上向下走的期望 向下走的$dp$不用多说 向上走的$dp$: 对于从$u$计算$v$的dp $dp[v]$应当是从u向周围引出所有路径减去走向t的路径的期望后再除以$deg_{u}-1$ 对于基环树: 环上的点很少. 此时环上的点的向上$dp$指从u出发向环上两头走的期望. 如何计算:对于环上每一个点都向环的两头各dp一次取平均值

【BZOJ 2878】 [Noi2012]迷失游乐园

2878: [Noi2012]迷失游乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 415  Solved: 283 [Submit][Status] Description 放假了,小Z觉得呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现可以将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m只可能等于n或者n-1).小Z现在所在的大门也正好是

BZOJ 2878: [Noi2012]迷失游乐园

Writing now. 1 /************************************************************** 2 Problem: 2878 3 User: zrts 4 Language: C++ 5 Result: Accepted 6 Time:588 ms 7 Memory:10748 kb 8 ****************************************************************/ 9 10 #i

NOI2012 迷失游乐园

http://www.lydsy.com/JudgeOnline/problem.php?id=2878 比较容易的概率题. Case1~5: 这是一棵树. 我们求出每个点i度数du[i],只走子树的期望距离g[i]和不走子树的期望距离f[i],这比较好求. 然后累加即可. Case6~10: 图中有一个环,然后环上的点都是一棵树的根. 对于每棵树,我们同样求出每个点i度数du[i],只走子树的期望距离g[i]. 那么怎么求不走子树的期望距离f[i]呢? 我们先求环上的点的f[i]吧. 我们枚举

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

树形+基环树DP/数学期望 然而我并不会做…… 题解戳这里:http://blog.csdn.net/u011265346/article/details/46328543 好吧先考虑一个简单点的,当m=n-1时,整个是一个树形的结构,无根树我们一般还是转成有根树来处理……然后既然是无法回头的,那么我们可以定一下方向:向下或者向上(废话) 定义一下: son[x]为x的儿子的数量 down[x]表示从x这个点出发,向叶子们走的期望长度. 怎么算呢?其实就是所有可能的情况(所有的儿子)加起来,再求

BZOJ 2878([Noi2012]-失落的游乐园树DP+出站年轮加+后市展望DP+vector的erase)

2878: [Noi2012]迷失乐园 Time Limit: 10 Sec  Memory Limit: 512 MBSec  Special Judge Submit: 319  Solved: 223 [Submit][Status] Description 放假了,小Z认为呆在家里特别无聊,于是决定一个人去游乐园玩.进入游乐园后,小Z看了看游乐园的地图,发现能够将游乐园抽象成有n个景点.m条道路的无向连通图,且该图中至多有一个环(即m仅仅可能等于n或者n-1). 小Z如今所在的大门也正好