[BZOJ3470]Freda’s Walk

bzoj

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 \le n \le 10000,1\le m\le 100000,0 \le u,v<n,1\le w\le1000\)

sol

先正反建图跑出从每个点出发的期望行走步数\(f_i\)和从起点出发到达每个点的概率\(p_i\)
枚举删掉一条边\((u,v)\),那么\(f_u\)中相应的会减少\(f_v\)的贡献,但其他出边贡献的比例则增大了,又因为\(f_i\)的一个变化量\(\Delta\)导致的\(f_1\)的变化量是\(p_i\Delta\),所以可以\(O(1)\)计算删掉每条边对答案的影响,取\(\max\)即可。

code

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
int gi(){
	int x=0,w=1;char ch=getchar();
	while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
	if (ch=='-') w=0,ch=getchar();
	while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return w?x:-x;
}
const int N = 2e5+5;
int n,m,to[N],nxt[N],ww[N],hd1[N],hd2[N],cnt,du[N],sum[N];
double p[N],f[N],ans;
queue<int>Q;
void link(int v,int w,int &hd){
	to[++cnt]=v;nxt[cnt]=hd;ww[cnt]=w;hd=cnt;
}
int main(){
	n=gi();m=gi();
	for (int i=1;i<=m;++i){
		int u=gi()+1,v=gi()+1,w=gi();
		link(v,w,hd1[u]);link(u,w,hd2[v]);sum[u]+=w;
	}
	for (int i=1;i<=n;++i)
		for (int e=hd1[i];e;e=nxt[e])
			++du[to[e]];
	for (int i=1;i<=n;++i) if (!du[i]) Q.push(i);
	p[1]=1;
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		for (int e=hd1[u];e;e=nxt[e]){
			p[to[e]]+=p[u]*ww[e]/sum[u];
			if (!--du[to[e]]) Q.push(to[e]);
		}
	}
	for (int i=1;i<=n;++i)
		for (int e=hd2[i];e;e=nxt[e])
			++du[to[e]];
	for (int i=1;i<=n;++i) if (!du[i]) Q.push(i);
	while (!Q.empty()){
		int u=Q.front();Q.pop();
		for (int e=hd2[u];e;e=nxt[e]){
			f[to[e]]+=(f[u]+1)*ww[e]/sum[to[e]];
			if (!--du[to[e]]) Q.push(to[e]);
		}
	}
	for (int i=1;i<=n;++i)
		for (int e=hd1[i];e;e=nxt[e]){
			double tmp=(f[i]-(f[to[e]]+1)*ww[e]/sum[i])*sum[i]/(sum[i]-ww[e]);
			ans=max(ans,(tmp-f[i])*p[i]);
		}
	printf("%.6lf\n",ans+f[1]);
	return 0;
}
posted @ 2018-07-05 16:17  租酥雨  阅读(214)  评论(0编辑  收藏  举报