BZOJ 3470 Freda’s Walk (期望)

在一个有向无环图中,Freda从起点出发,选择边的概率取决于边的积水深度。允许忽略一条边,求从起点出发散步路径长度的期望最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3470: Freda’s Walk

Time Limit: 1 Sec Memory Limit: 128 MB
Description

雨后的Poetic Island空气格外清新,于是Freda和Rainbow出来散步。 Poetic Island的交通可以看作一张n个点、m 边的有向无环图。由于刚下过雨,每条边都有一个积水深度,而恰好Freda 和Rainbow都喜欢踩水玩儿,于是Ta们从某个点出发,选择走向哪条边的概率与该边的积水深度是成正比的。即:如果Freda和Rainbow现在在点u,点u 出发的所有边的积水深度之和为s,从u到v的边积水深度为w,那么Ta们选择走向v的概率就是 w/s。
Ta们会一直走下去,直到到达一个没有出边的点,那么散步的路程长度就是走过的边的数量。更特殊的是,Freda和Rainbow在出发之前还可以选择一条边,在散步过程中无视这条边的存在(当然也可以不选择)。请你帮忙计算一下,Ta 们从0号点出发,散步的路程长度的期望值最大是多少?

Input

第一行两个正整数 n、m。
接下来m行每行三个整数u、v、w,表示从u到v有一条无向边,积水深度为w。

Output

输出Freda和Rainbow散步的路程长度的最大期望值,四舍五入保留六位小数。

Sample Input

4 5

0 1 2

0 2 1

0 3 3

1 3 1

2 3 4

Sample Output

2.000000

HINT

对于 100% 的数据,2<=n<=10000,1<=m<=100000,0<=u,v

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define Maxn 100010
#define Maxm 1000010

struct node{
    int u, to, w, nxt, flag;
}ed[Maxm], ted[Maxm];

int head[Maxn], idc;
int sum[Maxn], fed[Maxn];
bool vis[Maxn];
double g[Maxn], f[Maxn];

void adde(int u, int v, int w){
    ed[++idc].u = u; ed[idc].to = v; ed[idc].w = w;
    ed[idc].nxt = head[u]; head[u] = idc; ed[idc].flag = 1;
    ted[idc].u = v; ted[idc].to = u; ted[idc].w = w; 
    ted[idc].nxt = fed[v]; fed[v] = idc;//建立反向边 
}

void dfs2(int u){
    if( vis[u] ) return;
    vis[u] = 1;
    if(u == 0) { g[u] = 1; return;}
    for(int i=fed[u]; i; i=ted[i].nxt){
        int v = ted[i].to;
        dfs2( v );
        g[u] += g[v] * ted[i].w * 1.0 / sum[v];//到达每个点的概率 
    }
}


void dfs(int u){
    if( vis[u] ) return;
    vis[u] = 1; f[u] = 0;
    for(int i=head[u]; i; i=ed[i].nxt) 
        if( ed[i].flag ){//
            int v = ed[i].to;
            dfs( v );
            f[u] += (f[v] + 1) * ed[i].w * 1.0 / sum[u];
        }
    return;
}

int main(){
    freopen("secretbase.in", "r", stdin);
    freopen("secretbase.out", "w", stdout);
    int n, m;
    scanf("%d%d", &n, &m);
    idc = 0;
    memset(head, 0, sizeof(head));
    for(int i=1; i<=m; i++){
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        sum[u] += w;
        adde(u, v, w);
    }
    memset(vis, 0, sizeof(vis));
    for(int i=0; i<n; i++) g[i] = 0;
    for(int i=0; i<n; i++) dfs2( i );
    memset(vis, 0, sizeof(vis));
    dfs( 0 );
    double mx = f[0];
    for(int i=1; i<=idc; i++){
        int x = ed[i].u, y = ed[i].to;
        double cc;
        if(sum[x] != ed[i].w) 
            cc = (f[x]*sum[x]*1.0/(sum[x]-ed[i].w)-(f[y]+1)*ed[i].w*1.0/(sum[x]-ed[i].w))-f[x];
        else cc = -f[x];//删掉这条边所做出的贡献 
        mx = max(mx, f[0] + g[x]*cc);
    }
    printf("%.6lf\n", mx);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值