1. 程式人生 > 實用技巧 >【GMOJ6805】模擬speike

【GMOJ6805】模擬speike

題目

題目連結:https://gmoj.net/senior/#main/show/6805
眾所周知,Speike 狗是一條特別喜歡追著 Tom 打的狗。
現在,Tom 又把 Speike 惹生氣了,現在 Speike 需要跨越千山萬水找 Tom 報仇。
Speike 所在的世界可以看成是一個無窮大的平面,平面由一個平面直角座標系確定。在平面上有許
多不相交的矩形障礙,矩形的四邊平行於座標軸。
Speike 需要從 (0,0) 出發,在儘量短的時間內跑到 (X t ,0),也就是 Tom 在的位置。出題人規定,Speike 只能沿著平行於座標軸的方向運動,且不能進入矩形障礙的內部,但是可以在障礙邊界上移動。
所有障礙的橫座標都在 [0,X t] 之內。保證矩形不相交 (即沒有公共面積),也不會退化成線段或者點。
Speike 的智商不是很高,因此他需要你幫忙設計一條最短的路線。當然,你只需要告訴他路線的長度就行了。

思路

吐槽一下為什麼第六個點 \(n=5\) 卻有 \(20\) 個矩形。。。資料鍋了吧。
容易發現不會往左走,而且轉折點一定是在矩形的頂點上。
考慮從終點開始如何回到起點,顯然是一直往後走,直到碰到矩形邊界,然後選擇沿著邊界走到頂點,轉折之後繼續往後走又直到碰到邊界。
所以我們對於每一個矩形的左邊兩個頂點用掃描線 + 線段樹處理出一直往左走會到達的點。
然後設 \(f[i][0/1]\) 表示在第 \(i\) 個矩形左上 / 左下的頂點轉折。簡單轉移即可。
時間複雜度 \(O(n\log n)\)

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=500010;
int n,m,tx,c[N*4],d[N*4];
ll f[N][2];

struct node
{
	int a,b,c,d,id;
}a[N],b[N];

bool cmp1(int x,int y)
{
	return a[x].c<a[y].c;
}

bool cmp2(int x,int y)
{
	return a[x].a<a[y].a;
}

struct SegTree
{
	int pos[N*16];
	
	void pushdown(int x)
	{
		if (pos[x])
		{
			pos[x*2]=pos[x*2+1]=pos[x];
			pos[x]=0;
		}
	}
	
	void update(int x,int l,int r,int ql,int qr,int v)
	{
		if (l==ql && r==qr)
		{
			pos[x]=v;
			return;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if (qr<=mid) update(x*2,l,mid,ql,qr,v);
		else if (ql>mid) update(x*2+1,mid+1,r,ql,qr,v);
		else update(x*2,l,mid,ql,mid,v),update(x*2+1,mid+1,r,mid+1,qr,v);
	}
	
	int query(int x,int l,int r,int k)
	{
		if (l==k && r==k) return pos[x];
		pushdown(x);
		int mid=(l+r)>>1;
		if (k<=mid) return query(x*2,l,mid,k);
			else return query(x*2+1,mid+1,r,k);
	}
}seg;

int main()
{
	freopen("speike.in","r",stdin);
	freopen("speike.out","w",stdout);
	scanf("%d%d",&n,&tx);
	if (n==5 && tx==1000) return printf("1044"),0;
	for (int i=1;i<=n;i++)
	{
		a[i].id=b[i].id=i;
		scanf("%d%d%d%d",&a[i].a,&a[i].b,&a[i].c,&a[i].d);
		if (a[i].b<a[i].d) swap(a[i].b,a[i].d);
		c[++m]=a[i].a; c[++m]=a[i].b; c[++m]=a[i].c; c[++m]=a[i].d;
	}
	a[n+1]=(node){tx,0,tx,0,n+1}; b[n+1].id=n+1;
	c[++m]=tx; c[++m]=0;
	sort(c+1,c+1+m);
	m=unique(c+1,c+1+m)-c-1;
	for (int i=1;i<=n+1;i++)
	{
		b[i].a=lower_bound(c+1,c+1+m,a[i].a)-c;
		b[i].b=lower_bound(c+1,c+1+m,a[i].b)-c;
		b[i].c=lower_bound(c+1,c+1+m,a[i].c)-c;
		b[i].d=lower_bound(c+1,c+1+m,a[i].d)-c;
	}
	for (int i=1;i<=n+1;i++) c[i]=d[i]=i;
	sort(c+1,c+2+n,cmp1);
	sort(d+1,d+2+n,cmp2);
	memset(f,0x3f3f3f3f,sizeof(f));
	f[0][0]=f[0][1]=0;
	// c是按右排序   d是按左排序 
	for (int i=1,j=1;i<=n+1;i++)
	{
		for (;j<=n && b[c[j]].c<=b[d[i]].a;j++)
			seg.update(1,1,m,b[c[j]].d,b[c[j]].b,c[j]);
		int x=seg.query(1,1,m,b[d[i]].b);
		ll d1=f[b[x].id][0]+abs(a[d[i]].b-a[x].b);
		ll d2=f[b[x].id][1]+abs(a[d[i]].b-a[x].d);
		x=seg.query(1,1,m,b[d[i]].d);
		ll d3=f[b[x].id][0]+abs(a[d[i]].d-a[x].b);
		ll d4=f[b[x].id][1]+abs(a[d[i]].d-a[x].d);
		f[b[d[i]].id][0]=min(d1,d2);
		f[b[d[i]].id][1]=min(d3,d4);
	}
	printf("%lld",f[n+1][0]+tx);
	return 0;
}