[bzoj3470]Freda’s Walk【概率与期望dp】
【题目链接】
https://www.lydsy.com/JudgeOnline/problem.php?id=3470
【题解】
正着做一遍求出从一号点到每个点的概率。
倒着做一遍求出每个点开始向后走期望走多远。
然后考虑去除一个点的一条出边后的影响,显然就是的变化量乘以到达这个点的概率,而概率是不变的,还是。
# 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;
}