[USACO08FEB] 酒店Hotel - 线段树

第一行输入n,m ,n代表有n个房间,编号为1---n,开始都为空房,m表示以下有m行操作,以下 每行先输入一个数 i ,表示一种操作:

若i为1,表示查询房间,再输入一个数x,表示在1--n 房间中找到长度为x的连续空房,输出连续x个房间中左端的房间号,尽量让这个房间号最小,若找不到长度为x的连续空房,输出0,并且在这x个空房间中住上人。

若i为2,表示退房,再输入两个数 x,y 代表 房间号 x---x+y-1 退房,即让房间为空。

Solution

显然用线段树维护,用类似最大子段和的方法处理

考虑 update,无非就是更新单侧值,然后再讨论三种情况更新一下 ans

比较揪心的是如何查询,我们考虑如何在查询时讨论

  • 如果到达递归边界就表明找到,返回

  • 如果左区间足够多就去左区间

  • 否则,如果中间足够多,就直接返回左区间从右开始的最长连续的左端点

  • 否则去右区间找

#include 
using namespace std;

const int N = 2000005;

int ml[N],mr[N],mx[N],len[N],tag[N];

void build(int p,int l,int r) {
    ml[p]=mr[p]=mx[p]=len[p]=r-l+1;
    if(lqr || r=ql&&r<=qr) {
        if(c) ml[p]=mr[p]=mx[p]=len[p], tag[p]=1;
        else ml[p]=mr[p]=mx[p]=0, tag[p]=-1;
    }
    else {
        pushdown(p);
        modify(p*2,l,(l+r)/2,ql,qr,c);
        modify(p*2+1,(l+r)/2+1,r,ql,qr,c);
        update(p);
    }
}

int query(int p,int l,int r,int ql,int qr,int k) {
    if(l==r) return l;
    pushdown(p);
    if(mx[p*2]>=k) return query(p*2,l,(l+r)/2,ql,qr,k);
    if(mr[p*2]+ml[p*2+1]>=k) return (l+r)/2-mr[p*2]+1;
    return query(p*2+1,(l+r)/2+1,r,ql,qr,k);
}

int n,m,t1,t2,t3;

int main() {
    scanf("%d%d",&n,&m);
    build(1,1,n);
    for(int i=1;i<=m;i++) {
        scanf("%d",&t1);
        if(t1==1) {
            scanf("%d",&t2);
            if(mx[1]>=t2) {
                t3=query(1,1,n,1,n,t2);
                printf("%d\n",t3);
                modify(1,1,n,t3,t3+t2-1,0);
            }
            else printf("0\n");
        }
        else {
            scanf("%d%d",&t2,&t3);
            modify(1,1,n,t2,t2+t3-1,1);
        }
    }
}

你可能感兴趣的:([USACO08FEB] 酒店Hotel - 线段树)