bzoj3470 Freda’s Walk

149 篇文章 0 订阅
18 篇文章 0 订阅

http://www.elijahqi.win/2018/01/11/bzoj3470-fredas-walk/
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<1<=w<=1000。

Source
Adera 2 杯省选模拟赛

假如没有删边的操作我的期望其实是个定值 那么我就需要去算一下删边之后带来期望的改变值了

我提前给我的序号都+1处理了因为题目中给的图是一个DAG 那么可以拓扑一下 首先从后往前拓扑 算出我当前这个节点到终点停止的期望 然后再正着拓扑一下 算出1号节点到达每个点的概率是多少 其实我只需要算1号节点的值就okay了 本想宽搜就好 但是这其实是一个dp如果直接宽搜可能存在后效性所以为了省事 其他初值直接给0 然后拓扑序dp一下 最后dp[1]就是我的答案的一种可能 接下来就是枚举我有哪些情况使得我的答案可能发生改变 那么 o(m)枚举每条边 这个针对答案的改变怎么算 首先针对我当前这个x节点 肯定是少了dp[y]w/sum[x]的期望 同时 由于少了这条边其他的概率也增大了 剩余的期望 每个期望变大的值sum[x]/(sum[x]-w) 求一下我新的期望的差值 最后对答案的贡献就是到这个点的概率*期望的该变量


#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 11000
#define M 110000
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();return x;
}
struct node{
    int y,z,next;
}data[M];
int num=0,h[N],in[N],x[M],y[M],w[M],n,m,sum[N];double dp[N],dp1[N];
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
}
int main(){
    freopen("bzoj3470.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=m;++i){
        x[i]=read()+1,y[i]=read()+1,w[i]=read();
        insert1(y[i],x[i],w[i]);++in[x[i]];sum[x[i]]+=w[i];
    }queue<int>q;
    for (int i=1;i<=n;++i) if (!in[i]) q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next) {
            int y=data[i].y,z=data[i].z;
            dp[y]+=(dp[x]+1)*z/sum[y];if(--in[y]==0) q.push(y);
        }
    }memset(h,0,sizeof(h));dp1[1]=1;num=0;
    for (int i=1;i<=m;++i) insert1(x[i],y[i],w[i]),++in[y[i]];
    for (int i=1;i<=n;++i) if (!in[i]) q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            dp1[y]+=dp1[x]*z/sum[x];if (--in[y]==0) q.push(y);
        }
    }double ans=dp[1];
    for (int i=1;i<=m;++i){
        double tmp=(dp[x[i]]-(dp[y[i]]+1)*w[i]/sum[x[i]])*sum[x[i]]/(sum[x[i]]-w[i])-dp[x[i]];
        ans=max(ans,dp[1]+tmp*dp1[x[i]]);
    }printf("%.6f",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值