[LOJ2759] JOI2014 Final 飞天鼠
问题描述
有n棵树,每棵树高度h[i],老鼠一开始在树1,高度为x。
有m条边,每条边(u,v,t),从树u到树v需要t的时间,并且到达树v之后高度会下降t。
在每棵树上时,可以花费1的时间使高度+1或-1。如果在树i,高度不能超过这棵树的高度。
现在老鼠想到n的顶端,求最短时间。
N<=1e5
解析
我们先贪心的想一想,最后的某条合法最短路径一定是长这样:先一直下降高度,直到高度为零,然后每次都上升到需要的高度后再飞。这样就不会出现明明能够飞、但是高度不够而导致花费不必要的代价的情况。这样我们在跑最短路的同时记录一下当前所在点的高度,然后讨论一下就可以了。
不要用自己定义的结构体跑优先队列,会很慢。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#define int long long
#define N 100002
#define M 300002
using namespace std;
int head[N],ver[M*2],nxt[M*2],edge[M*2],l;
int n,m,x,i,h[N],h2[N],dis[N];
priority_queue<pair<int,int> > q;
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y,int z)
{
l++;
ver[l]=y;
edge[l]=z;
nxt[l]=head[x];
head[x]=l;
}
void Dijkstra(int now)
{
memset(dis,0x3f,sizeof(dis));
q.push(make_pair(0,1));dis[1]=0;h2[1]=now;
while(!q.empty()){
int x=q.top().second,d=-q.top().first,h1=h2[x];
q.pop();
if(dis[x]!=d) continue;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i],w=dis[0],tmp;
if(h1==0&&edge[i]<=h[x]) w=2*edge[i],tmp=0;
else if(h1-edge[i]>h[y]) w=h1-h[y],tmp=h[y];
else if(h1-edge[i]<0&&edge[i]<=h[x]) w=2*edge[i]-h1,tmp=0;
else if(h1-edge[i]>=0&&h1-edge[i]<=h[y]) w=edge[i],tmp=h1-edge[i];
if(w!=dis[0]&&dis[y]>dis[x]+w){
dis[y]=dis[x]+w;h2[y]=tmp;
q.push(make_pair(-dis[y],y));
}
}
}
dis[n]+=abs(h[n]-h2[n]);
}
signed main()
{
n=read();m=read();x=read();
for(i=1;i<=n;i++) h[i]=read();
for(i=1;i<=m;i++){
int u=read(),v=read(),w=read();
insert(u,v,w);insert(v,u,w);
}
Dijkstra(x);
if(dis[n]>=dis[0]) puts("-1");
else printf("%lld\n",dis[n]);
return 0;
}