POJ 3667 Hotel(线段树:区间覆盖,维护最大连续子区间)

POJ 3667 Hotel(线段树:区间覆盖,维护最大连续子区间)

http://poj.org/problem?id=3667

题意:

        有一个线段,从1到n,下面m个操作,操作分两个类型,以1开头的是查询操作,以2开头的是更新操作

        1 w  表示在总区间内查询一个长度为w的可用区间,并且要最靠左,能找到的话返回这个区间的左端点并占用了这个区间,找不到返回0 。例如:n=10 , 1 3 查到的最左的长度为3的可用区间就是[1,3],返回1,并且该区间被占用了

        2 a len , 表示从单位a开始,清除一段长度为len的区间(将其变为可用,不被占用),不需要输出

分析:

       首先本题类似于UVA1400 ,所有区间合并类的线段树基本都需要维护3类信息:最大区间,最大前缀,最大后缀.

       维护一棵线段树,该线段树每个节点维护信息:在该节点所代表的区间内的覆盖情况cover: 为0时表示没有被覆盖,为1时表示被覆盖了,为-1时表示子节点中既有被覆盖的也有没被覆盖的.

最长连续区间长度sub,最长连续区间前缀长度pre,最长后缀suf.一共上面4种信息需要维护.其中cover是线段树的根本,sub,sufpre是方便快速查询.就像有setv ,addv sum的线段树一样. setvaddv是根本,sum是方便查询. sum的值要基于setvaddv.

当我们在[L,R]查找一个长度为8的连续空闲区间时,这个区间如果有连续8的空闲区间的话,要不就是在左儿子区间,要不就是在中间(即跨越了左儿子和右儿子),要不就是在右儿子区间.

       build(i,l,r)操作: 递归建立左右儿子,初始节点的cover都为0,sub=pre=suf=r-l+1.

       PushDown(i,num)操作: 如果cover位不是-1才PushDown.且子节点的sub pre 和suf根据cover位的值而变化.以前的cover值不变.

       PushUp(i,num)操作: 更新cover位,更新sub pre suf位.

       query(x,i,l,r)操作 : 首先如果sub<x直接返回-1,否则先PushDown后先在左子树查找,再在中间查找,再在右子树查找.过程中如果找到直接返回即可.

       update(ql,qr,v,i,l,r)操作:如果ql,qr区间包含了l,r区间,那么直接操作,否则PushDown操作后分别处理左右子树即可.最后还要PushUp.

AC代码:625ms

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN = 50000 + 100;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
#define root 1,1,n
int cover[MAXN * 4];
int sub[MAXN * 4], pre[MAXN * 4], suf[MAXN * 4];

void PushDown(int i, int num)
{
    if(cover[i] != -1)
    {
        cover[i * 2] = cover[i * 2 + 1] = cover[i];
        sub[i * 2] = pre[i * 2] = suf[i * 2] = (cover[i] ? 0 : (num - num / 2));
        sub[i * 2 + 1] = pre[i * 2 + 1] = suf[i * 2 + 1] = (cover[i] ? 0 : (num / 2));
    }
}
void PushUp(int i, int num)
{
    sub[i] = max(suf[i * 2] + pre[i * 2 + 1], max(sub[i * 2], sub[i * 2 + 1]));
    pre[i] = pre[i * 2];
    suf[i] = suf[i * 2 + 1];
    if(pre[i] == (num - num / 2)) pre[i] += pre[i * 2 + 1];
    if(suf[i] == num / 2) suf[i] += suf[i * 2];
    if(cover[i * 2] == -1 || cover[i * 2 + 1] == -1) cover[i] = -1;
    else if(cover[i * 2] != cover[i * 2 + 1]) cover[i] = -1;
    else cover[i] = cover[i * 2];
}
void build(int i, int l, int r)
{
    cover[i] = 0;
    sub[i] = pre[i] = suf[i] = r - l + 1;
    if(l == r)return ;
    int m = (l + r) / 2;
    build(lson);
    build(rson);
}
int query(int w, int i, int l, int r)
{
    if(sub[i] < w)return 0; //此句可以不要,在外面用sub[1]判断即可
    if(l == r) return l;
    PushDown(i, r - l + 1);
    int m = (l + r) / 2;
    if(w <= sub[i * 2]) return query(w, lson);
    else if(w <= suf[i * 2] + pre[i * 2 + 1]) return m - suf[i * 2] + 1;
    else return query(w, rson);
}
void update(int ql, int qr, int v, int i, int l, int r)
{
    if(ql <= l && r <= qr)
    {
        cover[i] = v;
        sub[i] = suf[i] = pre[i] = (v ? 0 : r - l + 1);
        return ;
    }
    PushDown(i, r - l + 1);
    int m = (r + l) / 2;
    if(ql <= m) update(ql, qr, v, lson);
    if(m < qr) update(ql, qr, v, rson);
    PushUp(i, r - l + 1);
}
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) == 2)
    {
        build(1, 1, n);
        while(m--)
        {
            int op, x, y;
            scanf("%d", &op);
            if(op == 1)
            {
                scanf("%d", &y);
                if(sub[1] < y)
                {
                    printf("0\n");
                    continue;
                }
                int p = query(y, root);
                printf("%d\n", p);
                update(p, p + y - 1, 1, root);
            }
            else
            {
                scanf("%d%d", &x, &y);
                update(x, x + y - 1, 0, root);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值