题解:NFLSOI#P10008. Speike和Tom

众所周知,Speike 狗是一条特别喜欢追着 Tom 打的狗。

现在,Tom 又把 Speike 惹生气了,现在 Speike 需要跨越千山万水找 Tom 报仇。

Speike 所在的世界可以看成是一个无穷大的平面,平面由一个平面直角坐标系确定。在平面上有许多不相交的矩形障碍,矩形的四边平行于坐标轴。

Speike 需要从 \((0,0)\) 出发,按恒定的速率在尽量短的时间内跑到 \((0,X_t)\) ,也就是 Tom 在的位置。出题人规定,Speike 只能沿着平行于坐标轴的方向运动,且不能进入矩形障碍的内部,但是可以在障碍边界上移动。

所有障碍的横坐标都在 \([0,X_t]\) 之内。保证矩形不相交(即没有公共面积,或者说公共面积为 0,但是可能共享一条边或者一个顶点),也不会退化成线段或者点。

Speike 的智商不是很高,因此他需要你帮忙设计一条最短的路线。当然,你只需要告诉他路线的长度就行了。

输入描述

  • 第一行一个整数 \(n\),代表障碍的个数。
  • 第二行一个整数 \(X_t\),代表终点的横坐标。
  • 第三行开始,共 \(n\) 行,每行 4 个整数 \(a,b,c,d\) ,代表每个矩形的某两个相对的顶点的坐标为 \((a,b)\)\((c,d)\)

输出描述

  • 共一行,一个整数,代表最短路线的长度。

\(\mathcal {SOLUTION}\)

我们可能会很开心地想到一个很假的做法来通过前面两个样例,也就是排除掉没有经过x轴的那些矩形,然后去找y坐标大于和小于零的情况下的两个最大值,然后去两个里面的小的那个,乘二加上 \(X_t\)

一眼假,错误性读者自证。

我们考虑正解,SXHdalao orz写了一个线段树维护DP,一个小时后他告诉我他假了。

但是不是不能使用我们的错解的想法:我们已经到过一个更高的点,那么我们肯定不需要再下沉去撞上一些不该撞的东西了。但是我们一开始在 \((0,0)\) 所以我们肯定是要往上( \(|y|\) 大的地方)走的,所以我们到一个“下一个块”的时候,我们往回看,找到那些高度比现在这个小的那些块,DP 它(言简意赅)。我们取那些块中能用最小代价到这个块的值,作为当前块的 DP 值。

这里有个关键性质,speike 需要一直往右走,不可能往左走,不然肯定不优,而且往右的总路程必定为 \(x_t\),那么能决定我们答案大小的值只会是在 \(y\) 轴上移动的距离。

我们思路已经基本清晰,那么先预处理所有的块,我们拿它的左右上下轮廓来表示这个矩形 \(\{xl,xr,yl,yr\},xl<xr,yl<lr\)。我们按照 \(xl\) 的大小升序排列,然后 \(1\)~\(\operatorname {n}\) 遍历。在一个 set 里面我们不需要存一整个块,不然数据比较复杂不好维护,我们要存一条单独的横向的(与 x 轴平行的)边的 y 值与从 \((0,0)\) 到它的最小距离。

遍历的过程中我们到第 \(i\) 个块的时候,从当前的 set 里面取出 lower_bound({a[i].yl,0}) 设为边 \(j\) (为什么是 \(a[i].yl\)? 其实是哪个都无所谓,我们选取一条作为主旋律而已), 然后得出它到块 \(i\) 的上边与下边的 \(y\) 轴相应数值,直接把这个边 \(j\) 去覆盖掉( erase 大法好!):

\[\mathcal{\Delta h_{below}=|a[i].y_{below}-y_j|}\\ \mathcal{\Delta h_{above}=|a[i].y_{above}-y_j|}\\ \mathcal{dp_{i_{below}}=\min \{\ \Delta h_{below}+dp[j]\ \} }\\ \mathcal{dp_{i_{above}}=\min \{\ \Delta h_{above}+dp[j]\ \} }\\ \]

我们最后简单地发现答案是:

\[\forall i\in [1,n]\\ ans=\min \{ dp[i]_{below\ or \ above} +|a[i].y_{below\ or\ above}| \} + X_t\\ \]

这个查找边的话只需要 set 暴力跳就好了,时间不管怎么样都可以卡过去,毕竟我们的 erase 操作保证了不会爆炸到 \(\mathcal {O(n^2)}\)

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=5e5+10;
const int INF=1e16;
int n,ed;
set<pair<int,int>>s;
struct node{
	int xl,xr,yl,yr;
}a[N];

bool cmp(node a,node b){
	return a.xl<b.xl;
}

signed main(){
	freopen("speike.in","r",stdin);
	freopen("speike.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin>>n>>ed;
	for(int i=1,xl,xr,yl,yr;i<=n;i++){
		cin>>xl>>yl>>xr>>yr;
		if(xl>xr)swap(xl,xr);
		if(yl>yr)swap(yl,yr);
		a[i]=node{xl,xr,yl,yr};
	}
	sort(a+1,a+1+n,cmp);
	s.insert({0,0});
	//set暴力维护 
	for(int i=1;i<=n;i++){
		auto it=s.lower_bound({a[i].yl,0});//在前面可以被选取(直接飞过来)的 
		int VL=INF,VR=INF;
		#define dp it->second
		#define h it->first
		while(it!=s.end() and h<=a[i].yr){
			//到下边的最小值 
			VL=min(VL,dp+llabs(h-a[i].yl)/*delta height for below*/);
			//到上边的最小值 
			VR=min(VR,dp+llabs(h-a[i].yr)/*delta height for above*/);
			s.erase(it);
			it=s.lower_bound({a[i].yl,0});
		}
		s.insert({a[i].yl,VL});
		s.insert({a[i].yr,VR});
	}
//	cout<<"debug"<<endl;
	int ans=INF;
	for(pair<int,int>t:s){
		int x=t.first;
		int y=t.second;
		ans=min(ans,llabs(x)+y);
	}
	cout<<ed+ans;
}
posted @ 2025-11-21 15:40  Noivelist  阅读(9)  评论(0)    收藏  举报