Fence Obstacle Course 题解
Fence Obstacle Course
题目描述
给定 \(n\) \((\le 10^5)\)个平台,第 \(i\) 个平台高度为 \(i\),左右两端点为 \(L_i\),\(R_i\) \((-10^5\le L_i \le R_i \le 10^5)\)。
你现在站在第 \(n\) 个平台上,你的横坐标为 \(S\) \((L_n\le S \le R_n)\),求一个移动方案(从一个平台的左右端点往下跳)使得你到达位置 \((0,0)\) (\(y=0\) 是一个无限长的平台)并且满足水平移动距离最小,输出这个最小水平移动距离。
题解
O(n^2) 暴力
设 \(f[i][0/1]\) 表示从 \(S\) 到第 \(i\) 块平台的左(0)/右(1)端点的最小值。
如果枚举第 \(i\) 块平台会从哪块平台跳下来的话复杂度过高,还要枚举横坐标并且很麻烦。
考虑第 \(i\) 块平台的左右端点会跳到那个平台,只需要枚举下面的平台 \(j\)。
遇到第一个满足 \(L_j\le L_i \le R_j\) 的平台 \(j\) 时就停下来,\(j\) 就是从平台 \(i\) 的左端点跳到的平台,右端点同理。
L[0]=R[0]=0;
for(int i=n;i>=1;i--){
for(int j=i-1;j>=0;j--)
if(L[j]<=L[i]&&L[i]<=R[j]){
f[j][0]=min(f[j][0],f[i][0]+abs(L[i]-L[j]));
f[j][1]=min(f[j][1],f[i][0]+abs(R[j]-L[i]));
break;
}
for(int j=i-1;j>=0;j--)
if(L[j]<=R[i]&&R[i]<=R[j]){
f[j][0]=min(f[j][0],f[i][1]+abs(R[i]-L[j]));
f[j][1]=min(f[j][1],f[i][1]+abs(R[i]-R[j]));
break;
}
}
cout<<f[0][0]<<'\n';
满分做法
上一个做法的瓶颈在于寻找 \(j\) ,可以通过线段树区间赋值,单点查询操作优化。
具体地,从小到大枚举平台,记录左右端点下面最靠上的平台(线段树中查找),将线段树中 \(L_i\) ~ \(R_i\) 赋值为 \(i\)。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int delta=100010;//把负坐标偏移成正的
int n,S,L[50010],R[50010],Lto[50010],Rto[50010];
LL f[50010][2];
struct SegmentTree
{int color,l,r;}st[900000];
inline int read()
{
int x=0;bool w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return w?-x:x;
}
void build(int p,int l,int r)
{
st[p].l=l;st[p].r=r;st[p].color=0;
if(l==r)return;
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void spread(int p)
{
if(!st[p].color)return;
st[p<<1].color=st[p<<1|1].color=st[p].color;
st[p].color=0;
}
int ask(int p,int pos)
{
if(st[p].l==st[p].r)return st[p].color;
spread(p);
int mid=(st[p].l+st[p].r)>>1;
if(pos<=mid)return ask(p<<1,pos);
return ask(p<<1|1,pos);
}
void change(int p,int l,int r,int c)
{
if(l<=st[p].l&&st[p].r<=r)return void(st[p].color=c);
spread(p);
int mid=(st[p].l+st[p].r)>>1;
if(l<=mid)change(p<<1,l,r,c);
if(mid<r)change(p<<1|1,l,r,c);
}
int main()
{
n=read();S=read()+delta;
L[0]=R[0]=delta;
for(int i=1;i<=n;i++){
L[i]=read()+delta;
R[i]=read()+delta;
}
build(1,1,200030);
for(int i=1;i<=n;i++){
Lto[i]=ask(1,L[i]);
Rto[i]=ask(1,R[i]);
change(1,L[i],R[i],i);
}
memset(f,0x3f,sizeof f);
f[n][0]=S-L[n];
f[n][1]=R[n]-S;
for(int i=n;i>=1;i--){
f[Lto[i]][0]=min(f[Lto[i]][0],f[i][0]+abs(L[i]-L[Lto[i]]));
f[Lto[i]][1]=min(f[Lto[i]][1],f[i][0]+abs(R[Lto[i]]-L[i]));
f[Rto[i]][0]=min(f[Rto[i]][0],f[i][1]+abs(R[i]-L[Rto[i]]));
f[Rto[i]][1]=min(f[Rto[i]][1],f[i][1]+abs(R[i]-R[Rto[i]]));
}
cout<<f[0][0]<<'\n';
}