【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;
}