[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;
}
posted @ 2020-11-13 22:29  CJlzf  阅读(132)  评论(0编辑  收藏  举报