Live2D

[线段树] (h) poj3667 (区间合并)

poj3667 Hotel

如在阅读本文时遇到不懂的部分,请在评论区询问,或跳转 线段树总介绍

 


【题目大意】

有一个旅馆,有N个房间排成一排,现在有两种操作,第一是有X个顾客要入住连续的X个房间,
要求输出最小的左端点的位置,不能满足就输出0,第二是将以L开始,长度为X的连续房间清空。

【输入文件】

第一行两个数N,M,表示房间数和操作数

接下来M行,每行有两种情况:

1 X 表示操作1

2 L X 表示操作2

【输出文件】

对于每一个1操作,输出答案。

 

题即为求最靠左的连续区间并置满以及把一段区间置空。

那么我们的线段树正式进入了区间合并的部分

 

0表示没有牛,1表示住了牛

用pre(前驱)表示该区间内的前导零个数,last(后继,用suf比较好但当时写了last不想改了,一个意思),表示后导零个数,Maxilen表示区间内最大连续0的个数

,tag表示该区间内全部置为tag(初值为-1)

 

pushup注意特殊情况,若 左/右 区间的 前驱/后继 完全覆盖了 左/右 区间,要特殊处理

if(pre[LS]==mid-l+1)pre[rt]=pre[RS]+pre[LS];
else pre[rt]=pre[LS];
if(last[RS]==r-mid)last[rt]=last[RS]+last[LS];
else last[rt]=last[RS];
Maxilen[rt]=max (Maxilen[LS] ,Maxilen[RS] ,pre[RS]+last[LS] );
//最大连续区间可能在 左边/右边/中间(所以为什么维护前驱后继)

 

pushdown

整段操作,赋0 or 赋1

 tag[LS]=tag[RS]=tag[rt];
 MaxiLen[LS]=pre[LS]=last[LS]=!tag[rt]?(mid-l+1):0;
 MaxiLen[RS]=pre[RS]=last[RS]=!tag[rt]?(r-mid):0;
 tag[rt]=-1; 

 

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
using namespace std;
const int N=2e6+3;
int pre[N<<2],last[N<<2],MaxiLen[N<<2],tag[N<<2];
int n,m,L;
//tag -1 -> Nothing 0 ; 0/1 -> get to 0/1 
#define LS (rt<<1)
#define RS (LS|1)
void pushup(int rt,int l,int r){
    MaxiLen[rt]=max(max(MaxiLen[LS],MaxiLen[RS]),pre[RS]+last[LS]);
    int mid=l+r>>1;
    if(pre[LS] == mid-l+1)pre[rt]=mid-l+1+pre[RS];
    else pre[rt]=pre[LS];
    if(last[RS] == r-mid)last[rt]=r-mid+last[LS];
    else last[rt]=last[RS];
}
void build(int rt,int l,int r){
    pre[rt]=last[rt]=MaxiLen[rt]=r-l+1;
    tag[rt]=-1;
    if(l==r)return;
    int mid=l+r>>1;
    build(LS,l,mid);
    build(RS,mid+1,r);
    //pushup(rt,l,r);
}
void pushdown(int rt,int l,int r){
    if(tag[rt] == -1)return;
    tag[LS]=tag[RS]=tag[rt];
    int mid=l+r>>1;
    MaxiLen[LS]=pre[LS]=last[LS]=!tag[rt]?(mid-l+1):0;
    MaxiLen[RS]=pre[RS]=last[RS]=!tag[rt]?(r-mid):0;
    tag[rt]=-1; return;
}
void update(int rt,int l,int r,int x,int y,int p){
    if(l>=x&&r<=y){
        tag[rt]=p; //直接覆盖
        MaxiLen[rt]=!p?(r-l+1):0;
        pre[rt]=last[rt]=!p?(r-l+1):0;
        return;
    }pushdown(rt,l,r);
    int mid=l+r>>1;
    if(x<=mid) update(LS,l,mid,x,y,p);
    if(y>mid) update(RS,mid+1,r,x,y,p);
    pushup(rt,l,r); return;
}
int query(int rt,int l,int r,int LEN){
    if(l==r)return l;  //一个点,因为一直优先往左,所以这个点一定是最靠左的
    int mid=l+r>>1;
    pushdown(rt,l,r);
    if(MaxiLen[LS]>=LEN) return query(LS,l,mid,LEN);
    if(last[LS]+pre[RS]>=LEN) return mid-last[LS]+1; //取得左儿子后继的开端
    if(MaxiLen[RS]>=LEN) return query(RS,mid+1,r,LEN);
    return 0; //if(MaxiLen[rt]<LEN)return 0;
}
int main(){
    scanf("%d%d",&n,&m);
        build(1,1,n);
        int ty,x,L;
        while(m--){
            scanf("%d",&ty);
            if(ty==1){
                scanf("%d",&x);
                L=query(1,1,n,x);
                printf("%d\n",L);
                if(L)update(1,1,n,L,L+x-1,1);
            }else {
                scanf("%d%d",&L,&x);
                update(1,1,n,L,L+x-1,0);
            }
        }
    
    return 0;
}

颜色是不是很好看qwq

End

posted @ 2019-07-22 22:51  lsy263  阅读(173)  评论(0编辑  收藏  举报