JZOJ 6805. NOIP2020.9.26模拟speike【线段树】【dp】


题意:

在一个平面坐标系里有 n n n个矩形障碍,我们只能沿着障碍的边界移动或沿着平行于坐标轴的位置移动
给出一个终点 ( x , 0 ) (x,0) (x,0)
问最少时间是多少


分析:

我们可以想到的,我们移动时横坐标一定是单调递增的,向右移动时如果遇到障碍,那肯定是往最靠近当前位置的边移动然后沿边走,如果没遇到障碍,那还等啥,接着走呀
因为如此,我们可以用线段树来维护离当前位置最近的边的位置进行转移


代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
using namespace std;
inline LL read()
{
    
    
	LL s=0,f=1; char c=getchar();
	while(c<'0'||c>'9') {
    
    if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {
    
    s=s*10+c-'0';c=getchar();}
	return s*f;
}
struct node{
    
    
	int a,b,c,d;
}s[500005];
int y[1000005];
bool cmp(node x,node y) {
    
    return x.a<y.a;}
int g;
struct Tree{
    
    
	int w[4000005];
	void add(int k,int L,int R,int l,int r,int v)
	{
    
    
		if(l<=L&&R<=r) {
    
    w[k]=v;return;}
		int mid=(L+R)>>1;
		if(l<=mid) add(k*2,L,mid,l,r,v);
		if(r>mid) add(k*2+1,mid+1,R,l,r,v);
		return;
	}
	void ask(int k,int L,int R,int p)
	{
    
    
		g=max(g,w[k]);
		if(L==R) return;
		int mid=(L+R)>>1;
		if(p<=mid) ask(k*2,L,mid,p);
		else ask(k*2+1,mid+1,R,p);
		return;
	}
}t;
int f[1000005][2];
int main()
{
    
    
//	freopen("speike.in","r",stdin);
//	freopen("speike.out","w",stdout);
	int n=read(),xt=read(),len=0;
	for(int i=1;i<=n;i++)
	{
    
    
		int a=read(),b=read(),c=read(),d=read();
		if(a>c) swap(a,c); if(b>d) swap(b,d);
		s[i]=(node){
    
    a,b,c,d};
		y[++len]=b;y[++len]=d;
	}
	y[++len]=0;
	sort(y+1,y+1+len);
	int m=unique(y+1,y+1+len)-y-1;
	s[++n]=(node){
    
    xt,0,xt,0};
	sort(s+1,s+1+n,cmp);
	for(int i=1;i<=n;i++)
	{
    
    
		int a=lower_bound(y+1,y+1+m,s[i].b)-y,b=lower_bound(y+1,y+1+m,s[i].d)-y;
		g=0;t.ask(1,1,m,a);
		f[i][0]=min(f[g][0]+abs(s[g].b-s[i].b),f[g][1]+abs(s[g].d-s[i].b));
		g=0;t.ask(1,1,m,b);
		f[i][1]=min(f[g][0]+abs(s[g].b-s[i].d),f[g][1]+abs(s[g].d-s[i].d ));
		t.add(1,1,m,a,b,i);
	}
	cout<<f[n][0]+xt;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35786326/article/details/109083299