版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/82932474
题目
题解
线段树优化DP
设f[i][0/1]表示在通过第i条栅栏后,处于栅栏左边/右边的最小路径长。
因为奶牛是直线下来的,所以最优方案当然是从上一个栅栏的这个位置下来。由于有栅栏的影响,奶牛们不能顺利的下来,此时到达这个位置的最优策略要么是从前面那个栅栏的左端点过来,要么从右端点过来。所以有,。
其中的j就是上一个挡住了这个位置的栅栏。我们可以用线段树来维护这个栅栏的编号。当栅栏(l[i],r[i]),出现后,我们把线段树上(l[i],r[i])这段区间改成i,表示这个位置是栅栏i阻挡了。对于后面的栅栏,修改时直接覆盖前面的信息即可。我们只要实现一个改段求点的线段树即可。
特别的,线段树初始值为0。一个位置如果得到的j=0,那么说明它前面没有栅栏,它可以直接从s过来,路径=abs(s-p)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rt 1,1,tot
using namespace std;
const int maxn=50010,maxtr=maxn*2*4;
int hh[maxtr],la[maxtr];
void update(int x)
{
if(la[x]==-1) return ;
int lc=x<<1,rc=lc|1;
hh[lc]=hh[rc]=la[x];
la[lc]=la[rc]=la[x];
la[x]=-1;
}
void change(int x,int xl,int xr,int l,int r,int c)
{
if(xl==l && xr==r)
{
hh[x]=la[x]=c;
return ;
}
int mid=xl+xr>>1;
int lc=x<<1,rc=lc|1;
update(x);
if(r<=mid) change(lc,xl,mid,l,r,c);
else if(mid<l) change(rc,mid+1,xr,l,r,c);
else change(lc,xl,mid,l,mid,c),change(rc,mid+1,xr,mid+1,r,c);
}
int ask(int x,int xl,int xr,int p)
{
if(xl==xr)
{
return hh[x];
}
int mid=xl+xr>>1;
int lc=x<<1,rc=lc|1;
update(x);
if(p<=mid) return ask(lc,xl,mid,p);
else return ask(rc,mid+1,xr,p);
}
int l[maxn],r[maxn];
int a[maxn*2];int tot=0;
int ul[maxn],ur[maxn];
int f[maxn][2];
int main()
{
int n,s;
scanf("%d%d",&n,&s);
for(int i=n;i>=1;i--)
{
scanf("%d%d",&l[i],&r[i]);
a[++tot]=l[i];a[++tot]=r[i];
}
a[++tot]=0;a[++tot]=s;
sort(a+1,a+tot+1);tot=unique(a+1,a+tot+1)-(a+1);
for(int i=1;i<=n;i++) ul[i]=lower_bound(a+1,a+tot+1,l[i])-a,ur[i]=lower_bound(a+1,a+tot+1,r[i])-a;
for(int i=1;i<=n;i++)
{
int fl=ask(rt,ul[i]),fr=ask(rt,ur[i]);
if(fl==0) f[i][0]=abs(s-l[i]);
else f[i][0]=min(f[fl][0]+abs(l[fl]-l[i]),f[fl][1]+abs(r[fl]-l[i]));
if(fr==0) f[i][1]=abs(s-r[i]);
else f[i][1]=min(f[fr][0]+abs(l[fr]-r[i]),f[fr][1]+abs(r[fr]-r[i]));
change(rt,ul[i],ur[i],i);
}
int e=lower_bound(a+1,a+tot+1,0)-a;
int ff=ask(rt,e);
if(ff==0) printf("%d\n",abs(s));
else printf("%d\n",min(f[ff][0]+abs(l[ff]),f[ff][1]+abs(r[ff])));
return 0;
}