【题解】POJ3667 Hotel

传送门
一眼就知道是线段树区间合并,模板在,我就不解释了。
在这道题中, q u e r y ( ) 被改成了 i n s e r t ( ) ,现在来讲讲如何实现 i n s e r t ( )


因为2操作就是 s e t ( ) ,所以自动略过。
考虑1操作:
首先在读入X后,用区间最大连续区间(即sum[1])与x进行比较,如果 s u m [ 1 ] < x ,那直接输出0就可以了,否则执行 i n s e r t ( )
由于题目要求的是空区间要尽量靠左,所以查找的时候优先考虑左边,因此分三种情况:

  • 1.当前线段树只有一个结点时,直接返回(递归边界,面对 x == 1 的情况)
  • 2.所求区间全部位于左子树时,递归左子树。判定条件就是 s u m [ l c ( o ) ] >= x ,即左子树最大连续空区间长度大于 x ,这样做可以让区间尽量靠左,使答案尽量优。
  • 3.所求区间横跨左右子树时,直接返回位置。判定条件是 r s u m [ l c ( o ) ] + l s u m [ r c ( o ) ] >= x ,这种情况是在左子树无法满足要求时进行检查,相对于第4种情况要更优。返回值是 m i d r s u m [ l c ( o ) ] + 1
  • 4.当前三种情况都不满足时执行,递归右子树。由于执行 i n s e r t ( ) 前已经确认了解是存在的,所以可以直接进行递归,并可以保证一定可以找到最优解。

C o d e

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define max(x, y) ((x) > (y) ? (x) : (y))
struct SegTree {   //和模板中0和1掉了个头
    #define lc(o) o << 1
    #define rc(o) o << 1 | 1
    #define mid ((l + r) >> 1)
    static const int MAXSIZE = 100000 + 5;
    int lsum[MAXSIZE << 2], rsum[MAXSIZE << 2], sum[MAXSIZE << 2], color[MAXSIZE << 2];
    void creat(int o, int l, int r, int value) {
        color[o] = value;
        lsum[o] = rsum[o] = sum[o] = value == 0 ? r - l + 1 : 0;
    }
    void pushdown(int o, int l, int r) {
        if (color[o] != -1) {
            creat(lc(o), l, mid, color[o]);
            creat(rc(o), mid + 1, r, color[o]);
        }
    }
    void pushup(int o, int l, int r) {
        lsum[o] = color[lc(o)] == 0 ? lsum[lc(o)] + lsum[rc(o)] : lsum[lc(o)];
        rsum[o] = color[rc(o)] == 0 ? rsum[lc(o)] + rsum[rc(o)] : rsum[rc(o)];
        sum[o] = max(rsum[lc(o)] + lsum[rc(o)], max(sum[lc(o)], sum[rc(o)]));
        if (sum[o] == 0) color[o] = 1; 
        else if (sum[o] == r - l + 1) color[o] = 0; 
        else color[o] = -1;
    }
    void set(int o, int l, int r, int L, int R, int value) {
        if (l > R || r < L) return;
        else if (L <= l && r <= R) creat(o, l, r, value);
        else {
            pushdown(o, l, r);
            set(lc(o), l, mid, L, R, value);
            set(rc(o), mid + 1, r, L, R, value);
            pushup(o, l, r);
        }
    }
    int insert(int o, int l, int r, int x) {
        if (l > r) return 0;
        if (l == r) return l; //情况1
        pushdown(o, l, r);
        if (sum[lc(o)] >= x) return insert(lc(o), l, mid, x); //情况2
        else if (rsum[lc(o)] + lsum[rc(o)] >= x) return mid - rsum[lc(o)] + 1;  //情况3
        else return insert(rc(o), mid + 1, r, x); //情况4
    }
    void build(int o, int l, int r) {
        if (l > r) return;
        else if (l == r) creat(o, l, r, 0);  
        else {
            build(lc(o), l, mid);
            build(rc(o), mid + 1, r);
            pushup(o, l, r);
        }
    }
}root;
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    root.build(1, 1, n);
    for (int i = 1; i <= m; i++) {
        int opt, x, y;
        scanf("%d", &opt);
        if (opt == 1) {
            scanf("%d", &x);
            if (root.sum[1] < x) { //确认存在满足的解
                puts("0");
                continue;
            }
            int ret = root.insert(1, 1, n, x); //找到解
            root.set(1, 1, n, ret, ret + x - 1, 1); //将找到的解得区间直接覆盖成1
            printf("%d\n", ret);
        }
        else {
            scanf("%d%d", &x, &y);
            root.set(1, 1, n, x, x + y - 1, 0); //覆盖为0
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/diogenes_/article/details/80519254