POJ - 3667 Hotel(线段树 区间合并 区间查询)

点我看题

题意:有L个连续的房间,日常会有一些人登记入住,也会有一些人退房,一次登记入住和一次退房都是连续的,问每次登记入住时,是否有连续的房间提供给入住者们,有的话,求入住的第一个房间序号.

分析:裸的线段树区间合并加查询,入住的时候,查询是否有满足条件的房间(st[1].sum>=所需要的房间数就是满足条件了),然后更新线段树,退房的话就更简单了,直接更新结点的长度就好.

参考代码:

/*区间合并*/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;
#define lson rt<<1
#define rson rt<<1|1
const int maxn = 5e4+10;
int n,q;
struct SegTree{
        int l,r;
        int sum;//当前结点保存的可入住人数的值
        int lsum,rsum;//当前结点
        int tag;//tag=1代表入住,-1代表退房,0代表不进行操作
};
SegTree st[maxn<<2];

//向上更新
void PushUp( int rt)
{
    st[rt].lsum = st[lson].lsum;
    st[rt].rsum = st[rson].rsum;
    int mid = (st[rt].l+st[rt].r)>>1;
    if( st[rt].lsum == mid-st[rt].l+1)
        st[rt].lsum += st[rson].lsum;
    if( st[rt].rsum == st[rt].r-mid)
        st[rt].rsum += st[lson].rsum;
    st[rt].sum = max(max(st[lson].sum,st[rson].sum),st[lson].rsum+st[rson].lsum);
}

//建树
void Build( int l, int r, int rt)
{
    st[rt].l = l;
    st[rt].r = r;
    st[rt].sum = r-l+1;
    st[rt].lsum = r-l+1;
    st[rt].rsum = r-l+1;
    st[rt].tag = 0;
    if( l == r)
        return;

    int mid = (l+r)>>1;
    Build(l,mid,lson);
    Build(mid+1,r,rson);
//    PushUp(rt);//这里不需要往上更新
}

//向下更新
void PushDown( int rt)
{
    if( st[rt].tag != 0)
    {
        st[lson].tag = st[rson].tag = st[rt].tag;
        if( st[rt].tag == 1)
        {
            st[lson].sum = st[lson].lsum = st[lson].rsum = 0;
            st[rson].sum = st[rson].lsum = st[rson].rsum = 0;
        }
        else if( st[rt].tag == -1)
        {
            st[lson].sum = st[lson].lsum = st[lson].rsum = st[lson].r-st[lson].l+1;
            st[rson].sum = st[rson].lsum = st[rson].rsum = st[rson].r-st[rson].l+1;
        }
        st[rt].tag = 0;
    }
}

//区间查询
//查询有没有区间长度至少为len的区间
//个人认为这里的l和r都可以通过rt得到,但是为了书写方便,还是给加上去了
int Query( int l, int r, int rt, int len)
{
    if( l == r)
        return l;
    PushDown(rt);//继续向下更新查找

    int mid = (l+r)>>1;
    if( st[lson].sum >= len)
        return Query(l,mid,lson,len);
    else if( st[lson].rsum+st[rson].lsum >= len)
        return mid-st[lson].rsum+1;
    else
        return Query(mid+1,r,rson,len);
}

//区间合并
void Update( int L, int R, int rt, int tag)
{
    if( st[rt].l == L && st[rt].r == R)
    {
        st[rt].tag = tag;
        if( tag == 1)
            st[rt].sum = st[rt].lsum = st[rt].rsum = 0;
        else if( tag == -1)
            st[rt].sum = st[rt].lsum = st[rt].rsum = st[rt].r-st[rt].l+1;
        return;
    }
    PushDown(rt);//向下更新

    int mid = (st[rt].l+st[rt].r)>>1;
    if( R <= mid)
        Update(L,R,lson,tag);
    else if( L > mid)
        Update(L,R,rson,tag);
    else
    {
        Update(L,mid,lson,tag);
        Update(mid+1,R,rson,tag);
    }
    PushUp(rt);//向上更新
}

int main()
{
    while( ~scanf("%d%d",&n,&q))
    {
        Build(1,n,1);
        while( q--)
        {
            int op;
            int x,y;
            scanf("%d",&op);
            if( op == 1)
            {
                scanf("%d",&x);
                if( x <= st[1].sum)
                {
                        int pos = Query(1,n,1,x);
                        printf("%d\n",pos);
                        //pos和pos+x-1分别表示更新的起始和结束的位置
                        //第一个1为根结点,第二个1为tag标记
                        Update(pos,pos+x-1,1,1);
                }
                else
                    puts("0");
            }
            else if( op == 2)
            {
                scanf("%d%d",&x,&y);
                //参数意义同上
                Update(x,x+y-1,1,-1);
            }
        }
    }

    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值