[省选模拟赛]path——期望+最短路
AubRain
2018-12-25 16:09:06
设 $f[i]$ 表示从 $i$ 走到 $n$ 的期望周数,答案就是 $f[1]$ 。
令 $du[i]$ 表示 $i$ 号点的度数,所以转移方程就是:
$$f[x]= \sum_{x->y} \frac{min(f[x],f[y])}{m}+\frac{m-du[x]}{m}*f[x] +1$$
化简一下就是:
$$f[x]=\frac{m+\sum_{x->y}min(f[x],f[y])}{du[x]}$$
然后我们的决策是只选择那些更优的 $f[y]$ ,即如果 $f[y]<f[x]$ 我们才用来更新 $f[x]$ 。然后那些不优秀的 $y$ 我们就当那些边不存在就行了。所以
$$f[x]=\frac{m+\sum_{x->y}f[y]}{du'[x]}$$
然后这就像最短路了~~(其实不像)~~。
用堆优化的 $dij$ ,然后每次选取 $f$ 值最小的节点去更新其它节点,这样就可以保证当前节点的 $f$ 值一定比被更新的节点的 $f$ 值更优秀。然后再更新的时候动态记录每个点的度数(被更新的次数)就行了。
**然后尝试证明一下:**
设
$$f[y]=\frac{m+f[a_1]+f[a_2]..+f[a_k]}{k}$$
那么 $f[y]$ 被 $f[x]$ 更新之后,就变成了
$$f'[y]=\frac{m+f[a_1]+f[a_2]..+f[a_k]+f[x]}{k+1}$$
我们只需要证明当 $f[x]<f[y]$ 时,$f'[y]<f[y]$ 即可。
~~显然。~~
证毕。
其实不看 $m$ 的话,$f[x]$ 相当于取了个平均数。有了 $m$ 也类似。所以就相当于 $f[x]$ 和 $f[y]$ 取了加权平均数得到了 $f'[y]$,自然有 $f[x]<f'[y]<f[y]$。
```cpp
#include<bits/stdc++.h>
#define N 100005
#define pr pair<double,int>
using namespace std;
inline void rd(int &x){
x=0;char ch=0;int w=0;
while(!isdigit(ch)) ch=getchar(),w|=ch=='-';
while( isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
x=w?-x:x;
}
int n,m;
double f[N],sum[N];
priority_queue<pr> q;
int head[N],cnt,v[N],d[N];
struct nd{int nxt,to;}e[N<<1];
#define For(x) for(int y,i=head[x];(y=e[i].to);i=e[i].nxt)
inline void add(int x,int y){
e[++cnt]=(nd){head[x],y};head[x]=cnt;
e[++cnt]=(nd){head[y],x};head[y]=cnt;
}
void DJ(){
while(q.size()){
int x=q.top().second;q.pop();
if(v[x]) continue; v[x]=1;
For(x) if(!v[y]){
d[y]++;sum[y]+=f[x];f[y]=(sum[y]+m)/d[y];
q.push(pr(-f[y],y));
}
}
}
signed main(){
rd(n);rd(m);
for(int x,y,i=1;i<=m;i++)
rd(x),rd(y),add(x,y);
q.push(pr(0,n));DJ();
printf("%.10lf",f[1]);
}
```