题目
传送门
分析
- 说下题意
题意说完你们应该就都懂了 - 如果牛在栅栏上,那么他只有当自己在 当前行 的左右端点时才能下降,且只能下降,不能左右移
- 如果牛在地上,那么他就可以满地乱跑,直到爬上栅栏
- 对于出口,我们可以把它当作在第一行,也可以当做在第0行
- 看完题面了,那么这个题就
没啥要说的了 - 设
d
p
[
i
]
[
0
/
1
]
表
示
在
第
i
行
的
左
端
/
右
端
,
到
终
点
的
距
离
dp[i][0/1]表示在第i行的左端/右端,到终点的距离
dp[i][0/1]表示在第i行的左端/右端,到终点的距离
- 如果从起点处理的话,不好维护到上面的距离
- 显然我们按照贪心的思想, 想要最小,那么尽可能让他走空白(这样代价为0),所以我们想知道在这个左端点/右端点下方最近的是第几行拦住了我们一往无前。
- 因为层层刷新,单独检查,所以线段树优化一下就好啦
代码
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
const int m=100000;
struct node{
int l,r;
int w,f;
}a[N<<2];
int n;
int ll[30010],rr[30010];
long long dp[30010][3];
inline int read()
{
int Num=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {Num=(Num<<3)+(Num<<1)+ch-'0'; ch=getchar();}
return Num*f;
}
void build(int k,int ll,int rr)
{
a[k].l=ll,a[k].r=rr;
if(a[k].l==a[k].r) return ;
int mid=(ll+rr)>>1;
build(k<<1,ll,mid); build(k<<1|1,mid+1,rr);
}
void push(int k)
{
a[k<<1].w=a[k<<1|1].w=a[k].w;
a[k<<1].f=a[k<<1|1].f=1; a[k].f=0;
}
void change(int k,int l,int r,int w)
{
if(l<=a[k].l&&a[k].r<=r)
{
a[k].w=w;
a[k].f=1;
return;
}
if(a[k].f) push(k);
int mid=(a[k].r+a[k].l)>>1;
if(mid>=l) change(k<<1,l,r,w);
if(mid<r) change(k<<1|1,l,r,w);
}
int ask(int k,int l)
{
if(a[k].l==a[k].r&&a[k].l==l) return a[k].w;
int mid=(a[k].l+a[k].r)>>1;
if(a[k].f) push(k);
if(mid>=l) return ask(k<<1,l);
else return ask(k<<1|1,l);
}
int main()
{
n=read();
int s=read(); s+=m;
build(1,1,N);
ll[0]=m,rr[0]=m;
for(int i=1;i<=n;i++)
{
ll[i]=read(),rr[i]=read(); ll[i]+=m; rr[i]+=m;
int last_l=ask(1,ll[i]);
int last_r=ask(1,rr[i]);
dp[i][0]=min(dp[last_l][0]+abs(ll[last_l]-ll[i]),dp[last_l][1]+abs(rr[last_l]-ll[i]));
dp[i][1]=min(dp[last_r][0]+abs(ll[last_r]-rr[i]),dp[last_r][1]+abs(rr[last_r]-rr[i]));
change(1,ll[i],rr[i],i);
}
printf("%d ",min(dp[n][0]+abs(ll[n]-s),dp[n][1]+abs(rr[n]-s)));
return 0;
}