【BZOJ】栅栏-线段树优化DP

蒟蒻做了一天线段树,表示心态炸裂,从未想到线段树还有这么多操作(已狗带)。
传送门:BZOJ3387


题意

    Farmer John 为奶牛们设置了一个障碍赛。障碍赛中有n个(n≤50000)各种长度的栅栏,每个都与x轴平行,其中的第i个栅栏的y坐标为i。
    终点在坐标原点(0,0),起点在(s,n)。如下所示,×为终点,s为起点。

    奶牛们很笨拙,它们都是绕过栅栏而不是跳过去的。也就是说它们会沿着栅栏走直到走到栅栏头,然后向着x轴奔跑,直到碰到下一个栅栏拦住去路,然后再绕过去……
    求奶牛从起点到终点需要走的最短水平距离。


数据范围

n≤50000 |s|≤100000
-100000≤ai<bi≤100000,an≤s≤bn (ai,bi分别代表第i个栅栏的两个端点的横坐标)


题解

    本蒟蒻怎么可能会做?orzzzzz
    于是自己yy了一种方法,居然依靠水数据得了七十分。
    就是这样的愚蠢。

#include
#include
#include
#include
#define INF 1e7
using namespace std;
const int N=5e4+10;
int n,s,ans;

struct P{
    int x;
    bool in;
}t[N<<2];
int fr,to;

inline int read()
{
    char c=getchar();int x=0,t=1;
    while(c<'0' || c>'9') {if(c=='-') t=-1;c=getchar();}
    while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*t;
}

bool cmp(P A,P B)
{
    return A.xinline int bs(int x){
    return x>0? x:-x;
}

inline int min(int a,int b)
{
    return a>b? b:a;
}

int main(){
    n=read();s=read();ans=INF;
    for(int i=0;i1].x=read();t[i<<1].in=true;
        t[i<<1|1].x=read();t[i<<1|1].in=false;
    }n<<=1;
    int now=0;
    sort(t,t+n,cmp);
    for(int i=0;iif(t[i].in){
          now++;
          if(now==1){
            if(t[i].x>s) break;
            fr=t[i].x;
          }
        }else{
            now--;
            if(now==0){
                if(t[i].xcontinue;
                to=t[i].x;
                ans=min(bs(s-fr)+bs(fr),ans);
                ans=min(bs(s-to)+bs(to),ans);
            }
        }
    }
    if(ans==INF){
        printf("%d\n",bs(s));
    }else 
       printf("%d\n",ans);
    return 0;
}
//a logical problem:
//it is actually not necessary for us to go to the farthest point
//so sometimes the answer may be larger than expected
//somestimes think so little and make problem more silly 

    错因本蒟蒻已经写在注释里了,不过相信各位大佬看一眼就知道了(或许有的大佬不屑于看)。
    下面讲讲正解。
    对于从上到下走的时候,每走到一个新栅栏端点,找到最短距离有四种可能,分别是前一个和这一个左右互相组合。Dp方程就不写了,网上到处都有。
    我们当然不会打n^2暴力dp。于是想一想(虽然我也没想出来),然后妙妙的事情就发生了!
    我们是不是可以把每个已经处理过的dp值都存在一个线段树里,线段树的范围就是坐标范围(反正装得下),然而标记一下当前栅栏用0,1两个状态来分别贮存左右端点过来的最短路,取个最小值就好了。
    所以这道题当然也有另一种做法,那就是dijkstra!
    然而由于本蒟蒻太弱了,连dp做法都是抄题解还自行理解了一下午(然而还不是很懂)才懂了的,其他方法大家还是自行搜索吧。


代码

#include 
#include 
#include 
#define lson x<<1
#define rson x<<1|1
using namespace std;
const int maxn=200005;
int n,m;
int s[maxn<<2][2],t[maxn<<2][2];//s->min t->1(covered) 2->uncovered 
void pushdown(int x,int p)
{
    if(t[x][p])
    {
        s[lson][p]=s[rson][p]=1<<30;
        t[lson][p]=t[rson][p]=1;
        t[x][p]=0;
    }
}

void updata(int l,int r,int x,int a,int b,int p)
{
    if(l==r)
    {
        s[x][p]=min(s[x][p],b);
        return ;
    }
    pushdown(x,p);
    int mid=l+r>>1;
    if(a<=mid)   updata(l,mid,lson,a,b,p);
    else    updata(mid+1,r,rson,a,b,p);
    s[x][p]=min(s[lson][p],s[rson][p]);
}
void cover(int l,int r,int x,int a,int b,int p)
{
    if(a<=l&&r<=b)
    {
        t[x][p]=1,s[x][p]=1<<30;
        return ;
    }
    pushdown(x,p);
    int mid=l+r>>1;
    if(a<=mid)   cover(l,mid,lson,a,b,p);
    if(b>mid)    cover(mid+1,r,rson,a,b,p);
    s[x][p]=min(s[lson][p],s[rson][p]);
}
int query(int l,int r,int x,int a,int b,int p)
{
    if(a<=l&&r<=b)    return s[x][p];
    pushdown(x,p);
    int mid=l+r>>1;
    if(b<=mid)   return query(l,mid,lson,a,b,p);
    if(a>mid)    return query(mid+1,r,rson,a,b,p);
    return min(query(l,mid,lson,a,b,p),query(mid+1,r,rson,a,b,p));
}
int main()
{
    scanf("%d%d",&n,&m);
    m+=100002;
    memset(s,0x3f,sizeof(s));
    updata(1,maxn,1,100002,-100002,0),updata(1,maxn,1,100002,100002,1);//初始化0 
    int i,a,b,ta,tb;
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&a,&b);
        a+=100002,b+=100002;
        ta=min(query(1,maxn,1,1,a,0)+a,query(1,maxn,1,a+1,maxn,1)-a);
        tb=min(query(1,maxn,1,1,b,0)+b,query(1,maxn,1,b+1,maxn,1)-b);
        updata(1,maxn,1,a,ta-a,0),updata(1,maxn,1,a,ta+a,1);
        updata(1,maxn,1,b,tb-b,0),updata(1,maxn,1,b,tb+b,1);
        if(b>a+1)    cover(1,maxn,1,a+1,b-1,0),cover(1,maxn,1,a+1,b-1,1);
    }
    printf("%d",min(query(1,maxn,1,1,m,0)+m,query(1,maxn,1,m+1,maxn,1)-m));
    return 0;
}

你可能感兴趣的:(【BZOJ】栅栏-线段树优化DP)