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

来源:互联网 发布:数据新闻大趋势 pdf 编辑:程序博客网 时间:2024/05/17 04:33

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;}
0 0