在一个同学的博客里面看到了这个题
然后写了一下
Description
Solution
定义 (f_i) 为从 (i) 到 (n) 的期望钱数(在学了这么久之后,终于试了一下逆推)
转移就是:
[f_{x}=frac{sumlimits_{(x,y)in E}min(f_x,f_y)}{du_x}+1
]
(取 (min) 就是直接扔掉重买)
然后发现这东西并不能按照常规的直接转移或者消元来做,但是长得很像 (spfa/Dijsktra)
那么就用 (Dijsktra) 做吧,从 (n) 开始,依次更新答案保证了每次取的 (f_{x}) 是较小的那个
设当前有 (p) 个点在 (x) 点之前更新了,这些点的 (f) 值为 (sum)
那么有:
[f_{x}=frac{f_{x} imes(du_x-p)+sum}{du[x]}+1
]
推一下就行了
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define reg register
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=3e5+10;
int n,m,head[N],cnt,tim[N],du[N];
struct edge{
int to,nxt;
}e[N<<1];
struct node{
double res;
int id;
bool operator <(const node &a) const
{
return res>a.res;
}
};
inline void add(int u,int v)
{
e[++cnt].to=v; e[cnt].nxt=head[u];
return head[u]=cnt,void();
}
priority_queue<node> q;
bool vis[N];
double sum[N],ans[N];
inline void dij()
{
q.push((node){0,n});
while(!q.empty())
{
int fr=q.top().id; q.pop();
if(vis[fr]) continue; vis[fr]=1;
for(reg int i=head[fr];i;i=e[i].nxt)
{
int t=e[i].to; if(vis[t]) continue;
++tim[t]; sum[t]+=ans[fr];
ans[t]=(du[t]+sum[t])/tim[t];
q.push((node){ans[t],t});
}
} return ;
}
signed main()
{
n=read(); m=read();
for(reg int i=1,u,v;i<=m;++i) u=read(),v=read(),add(u,v),add(v,u),du[u]++,++du[v];
dij(); printf("%.10lf
",ans[1]);
return 0;
}
}
signed main(){return yspm::main();}