基环外向树dp

2878: [Noi2012]迷失游乐园

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 1155   Solved: 625
[ Submit][ Status][ Discuss]

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

HINT

Source


fa数组:对于环上的点有2个父亲

f数组:向下递推

g数组:向上递推

#include"cmath"
#include"cstdio"
#include"iostream"
#include"cstring"
#include"algorithm"
#include"vector"
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
int n,m;
double fa[maxn],sz[maxn];
double f[maxn],g[maxn],ans;
bool vis[maxn],flag;
int mo[maxn],be;
int que[51],cnt,side[51];

struct edge{
    int v,nxt,c;
    edge(){}
    edge(int v, int nxt, int c) : v(v), nxt(nxt), c(c){}
}e[maxn];
int tail,head[maxn];

int getmo(int x)
{
    if(x == mo[x]) return x;
    return mo[x] = getmo(mo[x]);
}

void init()
{
    tail = -1;
    memset(head,-1,sizeof(head));
}

void add(int u,int v, int c)
{
    e[++tail] = edge(v,head[u],c);
    head[u] = tail;
}

void dfsdown(int u,int last)
{

    for(int i = head[u]; ~i; i = e[i].nxt){
        int v = e[i].v;
        if(vis[v] || v == last) continue;
        fa[v] = 1;
        dfsdown(v,u);
        sz[u] ++;
        f[u] += f[v]+e[i].c;
    }
    if(sz[u]) f[u] = f[u]/sz[u];
}

void dfsup(int u,int last)
{
    for(int i = head[u]; ~i; i = e[i].nxt){
        int v = e[i].v;
        if(vis[v] || v == last) continue;
        g[v] = e[i].c;
        if(sz[u] + fa[u] > 1)
            g[v] +=  (f[u]*sz[u] - f[v] - e[i].c + g[u]*fa[u]) /(sz[u]+fa[u]-1);
        dfsup(v,u);
    }
}

void getcir(int u, int last, int deep)
{
    que[deep] = u;
    for(int i = head[u];~i; i = e[i].nxt){
        int v = e[i].v;
        if(v == last) continue;
        side[deep] = e[i].c;
        if(v == be){
            cnt = deep;
            flag = 1;
            return;
        }
        getcir(v,u,deep+1);
        if(flag) return ;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    init();
    for(int i = 1; i <= n; i++) mo[i] = i;
    for(int i = 1; i<= m; i++){
        int u,v,c;
        scanf("%d%d%d",&u,&v,&c);
        add(u,v,c);
        add(v,u,c);
        int mu = getmo(u), mv = getmo(v);
        if(mu == mv){
            be = mu;
        }
        mo[mu] = mo[mv];
    }
    ans = 0;
    if(m == n-1){
        dfsdown(1,1);
        dfsup(1,0);
    }
    else{
        getcir(be,be,1);
        for(int i = 1; i <= cnt; i++){
            vis[que[i]] = 1;
            fa[que[i]] = 2;
        }
        for(int i = 1; i <= cnt; i++){
            que[i+cnt] = que[i];
            side[i+cnt] = side[i];
        }

        for(int i = 1; i <= cnt; i++)
            dfsdown(que[i],que[i]);
        for(int i = 1; i <= cnt; i++){
            double k = 1;
            int j;
            int u = que[i];
            for(j = i+1; j < cnt+i-1; j++){
                int v = que[j];
                g[u] += k*(side[j-1] + (f[v]*sz[v])/(sz[v]+1));
                k/=(sz[v]+1);
            }
            g[u] += k*(side[j-1]+f[que[j]]);

            k = 1;
            for(j = i+cnt-1; j > i+1; j--){
                int v = que[j];
                g[u] += k*(side[j] + (f[v]*sz[v])/(sz[v]+1));
                k/=(sz[v]+1);
            }
            g[u] += k*(side[j]+f[que[j]]);
            g[u] /= 2;
        }
        for(int i = 1; i<= cnt; i++)
            dfsup(que[i],que[i]);

    }

    for(int i = 1; i<= n; i++){
         ans += (f[i]*sz[i]+g[i]*fa[i])/(sz[i]+fa[i]);
    }
    printf("%.5lf\n",ans/n);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值