1. 程式人生 > >[bzoj3470]Freda’s Walk【概率與期望dp】

[bzoj3470]Freda’s Walk【概率與期望dp】

【題目連結】
  https://www.lydsy.com/JudgeOnline/problem.php?id=3470
【題解】
  正著做一遍求出從一號點到每個點的概率g[i]
  倒著做一遍求出每個點開始向後走期望走多遠f[i]
  然後考慮去除一個點i的一條出邊後的影響,顯然就是f[i]的變化量乘以到達這個點的概率,而概率是不變的,還是g[i]
  

# include <bits/stdc++.h>
# define    N       200010      
using namespace std;
struct Edge{
    int
data,next,vote; }e[N]; int head[N],n,m,place,tag[N],p[N]; double f[N],cnt[N],g[N],sum[N],num[N],ans,tot; int read(){ int tmp=0, fh=1; char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') fh=-1; ch=getchar();} while (ch>='0'&&ch<='9') {tmp=tmp*10+ch-'0'; ch=getchar();} return
tmp*fh; } void build(int u, int v, int w){ e[++place].data=v; e[place].next=head[u]; head[u]=place; e[place].vote=w; tag[v]++; } void tuopu(){ int pl=1, pr=0; for (int i=1; i<=n; i++) if (tag[i]==0) p[++pr]=i; while (pl<=pr){ int x=p[pl++]; for (int ed=head[x]; ed!=0
; ed=e[ed].next){ tag[e[ed].data]--; if (tag[e[ed].data]==0) p[++pr]=e[ed].data; } } } int main(){ n=read(), m=read(); for (int i=1; i<=m; i++){ int u=read()+1, v=read()+1, w=read(); build(u,v,w); } tuopu(); bool flag=false; g[1]=1; for (int i=1; i<=n; i++){ if (flag==false&&p[i]!=1) continue; flag=true; int x=p[i]; double sum=0; for (int ed=head[x]; ed!=0; ed=e[ed].next) sum=sum+e[ed].vote; for (int ed=head[x]; ed!=0; ed=e[ed].next) g[e[ed].data]=g[e[ed].data]+g[x]*e[ed].vote/sum; } for (int i=n; i>=1; i--){ int x=p[i]; sum[x]=0,cnt[x]=0,num[x]=0; for (int ed=head[x]; ed!=0; ed=e[ed].next){ sum[x]=sum[x]+e[ed].vote; cnt[x]=cnt[x]+e[ed].vote*f[e[ed].data]; num[x]++; } if (sum[x]!=0) f[x]=cnt[x]/sum[x]+1; } tot=ans=f[1]; for (int i=n; i>=1; i--){ int x=p[i]; if (num[x]<=1) continue; double now=tot-g[x]*f[x]; for (int ed=head[x]; ed!=0; ed=e[ed].next){ double nowsum=sum[x]-e[ed].vote, nowcnt=cnt[x]-e[ed].vote*f[e[ed].data]; ans=max(ans,now+g[x]*(nowcnt/nowsum+1)); } } printf("%.6lf\n",ans); return 0; }