[BZOJ5197] [CERC2017]Gambling Guide

[BZOJ5197] [CERC2017]Gambling Guide

题目链接

https://www.lydsy.com/JudgeOnline/problem.php?id=5197

Solution

据说这种题有套路...但是窝不会...所以窝看了题解才知道做的...

首先这种期望题一般状态是(f_x)表示(x)(n)的期望步数,由于要求最优策略,那么我们随机到一条边时从(f_x,f_v)里选一个最小的转移即可,具体的:

[f_x=frac{1}{d_x}sum_{vin son_x}(1+min(f_x,f_v)) ]

其中(f_n=0)

然后我么考虑从(n)号点开始更新其他的点,仿照( m Dijkstra)的形式。

我们假定一开始除了(n)号点以外其他的点(f_x=+infty),且每个(min)都选(f_x)

那么设当前枚举到的点为(x),边为((x,v)),那么如果(f_vgeqslant f_x)时,(v)号点的(min)会改选为(f_x),那么我们更新一下(f_v)就好了。

显然这样更新满足每次选到的都是最小的点,而且他不会再被更新。

复杂度(O(nlog n))

#include<bits/stdc++.h>
using namespace std;
  
void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
   
void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
  
#define lf double
#define ll long long 
  
#define pii pair<int,int >
#define vec vector<int >
  
#define pb push_back
#define mp make_pair
#define fr first
#define sc second
  
#define FOR(i,l,r) for(int i=l,i##_r=r;i<=i##_r;i++) 
  
const int maxn = 4e5+10;
const int inf = 1e9;
const lf eps = 1e-8;
const int mod = 1e9+7;
  
lf f[maxn],s[maxn];
int d[maxn],c[maxn],head[maxn],tot,n,m,vis[maxn];
  
struct edge{int to,nxt;}e[maxn<<1];
  
void ins(int u,int v) {e[++tot]=(edge){v,head[u]},head[u]=tot;}
  
priority_queue<pair<lf,int > > q;
  
int main() {
    read(n),read(m);
    for(int i=1,x,y;i<=m;i++) read(x),read(y),ins(x,y),ins(y,x),d[x]++,d[y]++;
    memset(f,127,sizeof f);f[n]=0;
    q.push(mp(0.0,n));
    while(!q.empty()) {
        int x=q.top().second;q.pop();
        if(vis[x]) continue;vis[x]=1;
        for(int v,i=head[x];i;i=e[i].nxt)
            if(f[v=e[i].to]>=f[x])
                c[v]++,s[v]+=f[x],f[v]=(s[v]+d[v])/(lf)c[v],q.push(mp(-f[v],v));
    }printf("%.10lf
",f[1]);
    return 0;
}
原文地址:https://www.cnblogs.com/hbyer/p/11086733.html