Luogu4745 Gambling Guide

在一个同学的博客里面看到了这个题

然后写了一下

Description

link

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();}


原文地址:https://www.cnblogs.com/yspm/p/13763526.html