Poj 3667 Hotel 【线段树最左空区间】

Hotel

Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 13034 Accepted: 5606

Description

The cows are journeying north to Thunder Bay in Canada to gain cultural enrichment and enjoy a vacation on the sunny shores of Lake Superior. Bessie, ever the competent travel agent, has named the Bullmoose Hotel on famed Cumberland Street as their vacation residence. This immense hotel has N (1 ≤ N ≤ 50,000) rooms all located on the same side of an extremely long hallway (all the better to see the lake, of course).

The cows and other visitors arrive in groups of size Di (1 ≤ Di ≤ N) and approach the front desk to check in. Each group i requests a set of Di contiguous rooms from Canmuu, the moose staffing the counter. He assigns them some set of consecutive room numbers r..r+Di-1 if they are available or, if no contiguous set of rooms is available, politely suggests alternate lodging. Canmuu always chooses the value of r to be the smallest possible.

Visitors also depart the hotel from groups of contiguous rooms. Checkout i has the parameters Xi and Di which specify the vacating of rooms Xi ..Xi +Di-1 (1 ≤ Xi ≤ N-Di+1). Some (or all) of those rooms might be empty before the checkout.

Your job is to assist Canmuu by processing M (1 ≤ M < 50,000) checkin/checkout requests. The hotel is initially unoccupied.

Input

  • Line 1: Two space-separated integers: N and M
  • Lines 2..M+1: Line i+1 contains request expressed as one of two possible formats: (a) Two space separated integers representing a check-in request: 1 and Di (b) Three space-separated integers representing a check-out: 2, Xi, and Di

Output

  • Lines 1…..: For each check-in request, output a single line with a single integer r, the first room in the contiguous sequence of rooms to be occupied. If the request cannot be satisfied, output 0.

Sample Input
10 6
1 3
1 3
1 3
1 3
2 5 5
1 6

Sample Output
1
4
7
0
5

题意:旅馆的N(1 ≤ n ≤ 50,000) 个房间初始时全为空。现在有M(1 ≤ m < 50,000) 个操作,分为两种:
(1)1 X,要求得到连续的X个房间,输出X个连续房间的最小编号;如果有多个X的连续房间,输出编号最小的一个;不存在输出0;
(2)2 X Y,将X开始的连续Y个房间退掉。
思路:节点设置本区间最大连续空闲区间,以及从左侧、右侧开始的连续最大空闲空间。更新时注意:
本区间不包含更新区间且本区间满或空时,要将这种状态传递给子区间;
更新返回时用左右子区间的状态更新本区间。
代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
const int M = 5e4+4;
using namespace std;

struct node{
    int left, right, len; //len表示当前节点的长度
    int maxlen;//表示当前空的房间数
    int leftlen, rightlen;//leftlen表示以左端点为起点的最大长度
    //rightlen表示以右端点为结束点的长度

    void init(int state){
        len = right-left+1;
        maxlen = state*len;
        leftlen = rightlen = maxlen;
    }
}a[M<<2];

void build(int left, int right, int pos){
    a[pos].left = left;
    a[pos].right = right;
    a[pos].init(1);
    if(left == right) return ;
    int mid = (left+right)>>1;
    build(left, mid, pos<<1);
    build(mid+1, right, pos<<1|1);//
}

int query(int need, int pos){
    if(a[pos].maxlen < need) return 0;//如果当前节点最大空房间小于need,就返回0
    //如果以左边断电为起点的空房间长度大于need就返回left
    if(a[pos].leftlen >= need) return a[pos].left;
    如果做孩子的最大空房间长度大于need,那么肯定在左孩子那里
    if(a[pos<<1].maxlen >= need) return query(need, pos<<1);
    如果左孩子的右边最大长度+右孩子左边最大长度大于need就输出左边孩子以左孩子右端点未结束点的长度为a[pos<<1].rightlen+1的点
    if(a[pos<<1].rightlen+a[pos<<1|1].leftlen >= need)
        return a[pos<<1].right-a[pos<<1].rightlen+1;
        //否则就在右孩子里
    return query(need, pos<<1|1);
}

void pushdown(int pos){
    //将全空或者满了的状态传递给子孩子
    if(a[pos].maxlen == a[pos].len||a[pos].maxlen == 0){
        int temp = (a[pos].maxlen == a[pos].len);
        a[pos<<1].init(temp);
        a[pos<<1|1].init(temp);
    }
}

void pushup(int pos){
    a[pos].leftlen = a[pos<<1].leftlen;
    //如果左边孩子全空那么就加上右孩子的以右孩子右端点为起始点的长度
    //a[pos<<1|1].leftlen;
    if(a[pos].leftlen == a[pos<<1].right-a[pos<<1].left+1){
        a[pos].leftlen += a[pos<<1|1].leftlen;
    }
    //同上理
    a[pos].rightlen = a[pos<<1|1].rightlen;
    if(a[pos].rightlen == a[pos<<1|1].right-a[pos<<1|1].left+1){
        a[pos].rightlen += a[pos<<1].rightlen;
    }
    int x = pos<<1, y = (pos<<1|1);
    a[pos].maxlen=max(a[x].maxlen, a[y].maxlen);
    a[pos].maxlen = max(a[pos].maxlen, max(a[pos].leftlen, a[pos].rightlen));
    a[pos].maxlen = max(a[x].rightlen+a[y].leftlen, a[pos].maxlen);
}

void update(int l, int r, int pos, int state){
    if(a[pos].left >= l&& r >= a[pos].right){
        a[pos].init(state); return ;
    }
    pushdown(pos);
    int mid = (a[pos].left+a[pos].right)>>1;
    if(r <= mid) update(l, r, pos<<1, state);
    else if(l > mid) update(l, r, pos<<1|1, state);
    else{
        update(l, mid, pos<<1, state);
        update(mid+1, r, pos<<1|1, state);
    }
    pushup(pos);
}

int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    build(1, n, 1);
    int a, b, c;
    for(int i = 0; i < m; ++ i){
        scanf("%d", &a);
        if(a == 1){
            scanf("%d", &b);
            int temp = query(b, 1);
            printf("%d\n", temp);
            if(temp) update(temp, temp+b-1, 1, 0);
        }
        else{
            scanf("%d%d", &b, &c);
            update(b, b+c-1, 1, 1);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值