bzoj5197:[CERC2017]Gambling Guide
传送门
好像概率期望也写过一些题了,但是没啥用,还是不会套路,看了题解才会写
首先设\(f[x]\)为\(x\)到\(n\)的期望最少步数,\(deg_x\)表示\(x\)的度数
不考虑不动,显然\(f[x]=\sum_{(x,y)\in E}\frac{f[y]+1}{deg_x}\)
由于可以不动,\(f[x]=\sum_{(x,y)\in E}\frac{\min(f[x],f[y])+1}{deg_x}\)
然后我们其实也就是只有当\(f[y]<f[x]\)时才会产生贡献
这个条件和\(\rm dijkstra\)的松弛操作很相似,所以可以用\(\rm dijkstra\)来计算
但是这个式子并不好维护,因为只有\(f[y]<f[x]\)的时候才会产生贡献,那么很多枚举都是无效的
所以我们可以改一下式子\(f[x]=\frac{deg_x+\sum_{(x,y)\in E,f[y]<f[x]}f[y]}{\sum_{(x,y)\in E}[f[y]<f[x]]}\)
这样就可以动态的维护了
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
void read(int &x){
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=3e5+10;bool vis[maxn];
int n,m,cnt,pre[maxn*2],nxt[maxn*2],h[maxn];
double f[maxn],sum[maxn],in[maxn],t[maxn];
struct oo{double x;int y;};
priority_queue<oo>q;
bool operator<(oo a,oo b){return a.x>b.x;}
void add(int x,int y){
pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,in[x]++;
pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt,in[y]++;
}
void dijkstra(){
memset(f,127,sizeof f);
f[n]=0,q.push((oo){f[n],n});
while(!q.empty()){
int x=q.top().y;q.pop();
if(vis[x])continue;vis[x]=1;
for(rg int i=h[x];i;i=nxt[i])
if(f[pre[i]]>=f[x]){
t[pre[i]]++,sum[pre[i]]+=f[x];
f[pre[i]]=(in[pre[i]]+sum[pre[i]])/t[pre[i]];
q.push((oo){f[pre[i]],pre[i]});
}
}
}
int main(){
read(n),read(m);
for(rg int i=1,x,y;i<=m;i++)read(x),read(y),add(x,y);
dijkstra(),printf("%.6lf",f[1]);
}