#DTOJ#5116 移动

9 篇文章 0 订阅
5 篇文章 0 订阅

题面

牛牛被困在了一个房间里,他可以看到房间的出口,但是想要到达出口,需要经过 n n n 道闸门。我们可以根据这些闸门离牛牛的距离进行编号,离牛牛最近的闸门记为 1 1 1 号闸门,离牛牛最远的记为 n n n 号闸门。

牛牛每秒都可以选择前进到下一闸门,后退到上一闸门,或者原地不动(从起点到第一道闸门,从第 n n n 道闸门到出口的时间也是一秒)。

这些闸门在一些时刻是关闭的,无法通行,剩下的时刻是开启的,可以通行。

注意:如果牛牛所在的位置有一个闸门即将关闭,他在此时选择原地不动,就会被闸门夹到,变成牛排。牛牛想在不变成牛排的前提下走到出口,他想知道最短需要多少秒才能走到出口,如果他永远无法走到出口,输出 − 1 -1 1

在每一秒内,首先牛牛进行移动,然后闸门进行开/关的动作。

数据范围

20 % 20 \% 20% 的数据,满足 1 ≤ N , M ≤ 20 1 \leq N, M \leq 20 1N,M20

60 % 60 \% 60% 的数据,满足 1 ≤ N , M ≤ 2000 1 \leq N, M \leq 2000 1N,M2000

100 % 100 \% 100% 的数据,满足 1 ≤ N , M ≤ 100000 , 1 ≤ a ≤ N , 1 ≤ b ≤ c ≤ 1 0 9 1 \leq N, M \leq 100000,1 \leq a \leq N, 1 \leq b \leq c \leq 10^9 1N,M100000,1aN,1bc109

题解

对于每一段不可走的区间,我们考虑其可走的区间。对于一段可走的区间,他可以向两边与他有交集部分的区间走,至于怎么判断有没有交集?由于区间不相交,将其按左端点排序,我们只需要判断其左端点在该区间内即可,还有一种情况,左端点在区间外,但是其右端点在区间内,由于这种区间最多只有一个,所以特判一下。由于左端点具有单调性,所以我们可以用二分查找。然后跑一遍最短路即可。

代码

#include<bits/stdc++.h>
#define inf 1<<50
#define ll long long
using namespace std;
const int N=4e5+10;
inline int read(){
	int k=0,f=1;
	char ch;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')k=k*10+ch-'0',ch=getchar();
	return k*f;
}
ll n,m,cnt,ans=6e9;
bool v[N];
struct Node{
	int a,b,id,dep;
};
bool cmp(const Node x,const Node y){return x.a<y.a;}
vector<ll > s[N];
vector<Node> qu[N],ck[N];
void puts(Node x){
	cout<<x.a<<" "<<x.b<<" "<<x.id<<" "<<x.dep<<endl;
}
//int tot=1,ver[N*2],nxt[N*2],fst[N];
inline void prework(){
	for(int i=1;i<=n;++i){
		int L=1,R=2e9;
		sort(qu[i].begin(),qu[i].end(),cmp);
		Node CK;
		for(int j=0;j<qu[i].size();++j){
			CK.a=L,CK.b=qu[i][j].a-1,CK.id=++cnt,CK.dep=i;
			if(qu[i][j].a>L)ck[i].push_back(CK);
			L=qu[i][j].b+1;
		}
		CK.a=L,CK.b=R,CK.id=++cnt,CK.dep=i;
		ck[i].push_back(CK);
		qu[i].clear();
		for(int j=0;j<ck[i].size();++j)qu[i].push_back(ck[i][j]);//,cout<<"FAQ "<<i<<" "<<j<<endl,puts(qu[i][j]);
	}
	for(int i=1;i<=n;++i){
		//cout<<i<<" "<<qu[i].size()<<endl;
		sort(qu[i].begin(),qu[i].end(),cmp);
		for(int j=0;j<qu[i].size();++j){
			//cout<<"qu "<<i<<" "<<j<<" ";puts(qu[i][j]);
			s[i].push_back(qu[i][j].a);
		}
		sort(s[i].begin(),s[i].end());
	}
	s[0].push_back(1);
}
queue<Node > q;
ll dis[N*4];
inline void SPFA(){
	memset(dis,0x3f,sizeof(dis));
	//cout<<dis[0]<<endl;
	Node p;
	p.a=1;p.b=2e9,p.id=0,p.dep=0;
	qu[0].push_back(p);v[0]=1;dis[0]=0;
	q.push(p);
	while(q.size()){
		Node x=q.front();q.pop();
		//cout<<"FAAAAA ";puts(x);cout<<"DIS "<<dis[x.id]<<endl;
		int L=x.a,R=x.b,D=x.dep,P=x.id;v[P]=0;
		if(D==n)continue;
		int l1=-1,l2=-1,r1=-1,r2=-1;
		l1=lower_bound(s[D+1].begin(),s[D+1].end(),L)-s[D+1].begin();
		if(l1&&qu[D+1][l1-1].b>=L)l1--;
		if(D)l2=lower_bound(s[D-1].begin(),s[D-1].end(),L)-s[D-1].begin();
		if(l2&&D&&qu[D-1][l2-1].b>=L)l2--;
		r1=upper_bound(s[D+1].begin(),s[D+1].end(),R+1)-s[D+1].begin()-1;
		
		if(D)r2=upper_bound(s[D-1].begin(),s[D-1].end(),R+1)-s[D-1].begin()-1;//,cout<<"RRRR "<<r2<<" "<<D-1<<" "<<R<<" "<<endl;
		//cout<<"L R "<<l1<<" "<<r1<<" "<<l2<<" "<<r2<<endl;
		for(int j=l1;j<=r1&&l1>=0&&r1>=0;++j){
		//	puts(qu[D+1][j]);
			ll P1=qu[D+1][j].id,L1=qu[D+1][j].a,R1=qu[D+1][j].b;
		//	cout<<"ck "<<dis[P]<<" "<<R1<<" "<<dis[P1]<<" "<<L1<<endl;
			if(dis[P]>=R1)continue;
			if(dis[P1]>max(dis[P]+1,L1)){
				dis[P1]=max(dis[P]+1,L1);
				if(!v[P1]){
					//cout<<"ck "<<P1<<endl;
					q.push(qu[D+1][j]);
					v[P1]=1;
				}
			}
		}
		for(int j=l2;j<=r2&&l2>=0&&r2>=0;++j){
			ll P2=qu[D-1][j].id,L2=qu[D-1][j].a,R2=qu[D-1][j].b;
			if(dis[P]>=R2)continue;
			if(dis[P2]>max(dis[P]+1,L2)){
				dis[P2]=max(dis[P]+1,L2);
				if(!v[P2]){
					q.push(qu[D-1][j]);
					v[P2]=1;
				}
			}
		}
	}
}
int main(){
	n=read();m=read();
	for(int i=1;i<=m;++i){
		int c=read(),a=read(),b=read();
		Node p1;
		p1.a=a,p1.b=b,p1.id=i,p1.dep=c;
		qu[c].push_back(p1);
	//	puts(p1);puts(p2);
	}
	prework();
	SPFA();
	for(int i=0;i<qu[n].size();++i){
		ans=min(ans,dis[qu[n][i].id]+1);
	}
	if(ans==6e9)ans=-1;
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值