P8914 [DMOI-R2] 梦境
警示后人,一定要注意是 YES
还是 Yes
!!!
最近 Atcoder 的比赛打多了,里面的题基本上是 Yes
,然后就导致今天比赛时 WA 的一脸疑惑,wssb。
Solution
关于本题解中变量的有关说明:\(d?\) 代表源点为 \(?\) 的单源最短路,如 \(ds\) 代表以 \(s\) 为源点的最短路。原题中的 \(F\) 在代码中为 \(t\)。
首先字典序最小的最短路很好处理,记 \(pre_i\) 为源点到 \(i\) 的最短路中 \(i\) 前面的那个点,初始化 \(pre_i=i\),在松弛过程中加一个相等时的特判即可。
if(ds[v]>ds[u]+w){
ds[v]=ds[u]+w;pre[v]=u;
q.push(mkp(-ds[v],v));
}
else if(ds[v]==ds[u]+w)
if(pre[v]>u)pre[v]=u;
查询路径就从 \(t\) 一直跳 \(pre\)。
注意怪物的目标是与小 A 相遇,而小 A 行走的路线是确定的,那么怪物必然会先走到路线上的某一点再沿着路线追小 A,所以不妨枚举这个点。
以下怪物和小 A 的移动均用他们的起点来描述。
就枚举描蓝的这些点,设其为 \(i\),分类讨论:
-
如果 \(b\) 到 \(i\) 了,\(s\) 还没到 \(i\),这时只有死路一条。追及问题,时间为 \(\Large\frac{ds_i+db_i}5\)。
-
\(b\) 到了 \(i\),\(s\) 也经过了 \(i\),这时就需要比较 \(s\) 和 \(b\) 哪个先到达 \(t\),即 \(\Large\frac {ds_t}{2}\) 和 \(\Large\frac {db_i+ds_t-ds_i}{3}\) 的大小,若前者大,那么会被抓住,时间化简后为 \(db_i-ds_i\)
-
若前者小,就能逃到 \(t\),这时怪物距 \(t\) 的距离化简后为 \(db_i-ds_i-\Large\frac {ds_t}{2}\)。
到这里就写完了,剩下的就是一些细节问题。这里特别说一下输出的问题,使用 cout 输出会出现精度误差,如 \(5.243e6\) 等输出,直接使用 printf 保留小数又不能符合整数直接输出的要求。但题目中说答案不会超过两位小数,所以我们直接把边权 \(\times 100\) (这时路径长度要开 long long),这样保证运算时一直是整数,输出时是 \(100\) 的倍数就保留整数,是 \(10\) 的倍数保留一位小数,否则保留两位小数。
Code
#include<bits/stdc++.h>
#define int long long
#define fi first
#define se second
#define pb push_back
#define mkp make_pair
using namespace std;
using ll=long long;
using pii=pair<int,int>;
using pll=pair<ll,ll>;
using ull=unsigned long long;
inline void read(int &x){
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<1)+(r<<3)+(ch^48),ch=getchar();
x=r*w;
}
const int N=2e5+7,inf=1e12+7;
vector<pii>edge[N];
int n,m,s,t,b,pre[N],path[N],cnt;
int db[N],ds[N];
bool bb[N];
priority_queue<pii>q;
void dij(int s){
memset(ds,63,sizeof ds);memset(bb,0,sizeof bb);
ds[s]=0;q.push(mkp(0,s));
for(int i=1;i<=n;i++)pre[i]=i;
while(!q.empty()){
int u=q.top().se;q.pop();
if(bb[u])continue;
bb[u]=1;
for(auto it:edge[u]){
int v=it.fi,w=it.se;
if(ds[v]>ds[u]+w){
ds[v]=ds[u]+w;pre[v]=u;
q.push(mkp(-ds[v],v));
}
else if(ds[v]==ds[u]+w)
if(pre[v]>u)pre[v]=u;
}
}
}
void dij2(int s){
memset(db,63,sizeof db);memset(bb,0,sizeof bb);
db[s]=0;q.push(mkp(0,s));
while(!q.empty()){
int u=q.top().se;q.pop();
if(bb[u])continue;
bb[u]=1;
for(auto it:edge[u]){
int v=it.fi,w=it.se;
if(db[v]>db[u]+w){db[v]=db[u]+w;q.push(mkp(-db[v],v));}
}
}
}
void write(int x){
if(x%100==0)cout<<x/100;
else if(x%10==0)printf("%.1lf",x/100.0);
else printf("%.2lf",x/100.0);
}
main(){
read(n);read(m);read(s);read(b);read(t);
for(int i=1,x,y,z;i<=m;i++)
read(x),read(y),read(z),edge[x].pb(mkp(y,z*100)),edge[y].pb(mkp(x,z*100));
dij(s);
int tmp=t;
while(tmp!=s)path[++cnt]=tmp,tmp=pre[tmp];
path[++cnt]=s;
dij2(b);
bool flag=1;
int ans=inf,ans1=inf;
for(int i=1;i<=cnt;i++){
if(db[path[i]]/3.0<=ds[path[i]]/2.0)
flag=0,ans=min(ans,(db[path[i]]+ds[path[i]])/5);
else if((db[path[i]]+ds[t]-ds[path[i]])/3.0<=ds[t]/2.0)
flag=0,ans=min(ans,db[path[i]]-ds[path[i]]);
else if(flag)
ans1=min(ans1,db[path[i]]-ds[path[i]]-ds[t]/2);
}
if(flag)puts("YES"),write(ans1);
else puts("NO"),write(ans);
return 0;
}