poj 3667 Hotel(区间合并)

题目链接; poj 3667 Hotel

题意:有n个房间,m组询问,询问中第一个数字表示方式,1表示住人,后面的那个数表示住店的人数,2表示清空,后面跟着两个数字票p,q,表示从第p个房间开始数,q个房间清空,我们尽可能的要使人住在左边的房间且房间数必须连续,问你住店时从那个房间开始住;

#include<iostream>
#include<cstdio>
#define maxn 55555
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
int col[maxn<<2];//延迟标记,值为1时表示住人,0表示走人,-1表示此区间没有访问过,不用向下更新
int lsum[maxn<<2],rsum[maxn<<2],msum[maxn<<2];//lsum表示区间从最左边开始到右边的连续空位,rsum为区间从最右边开始到左边的连续空位,msum表示此区间最大的连续空间
void pushdown(int rt,int m)
{
    if(col[rt]!=-1) //此区间之前被更新过,所以要向下更新
    {
       col[rt<<1]=col[rt<<1|1]=col[rt];//把此区间的的信息传递给它的子区间
       lsum[rt<<1]=rsum[rt<<1]=msum[rt<<1]=col[rt]?0:(m-(m>>1));//左儿子,两种情况,一种col为1表示住人,空位变为0,否则就是走人,空位等于区间长度
       lsum[rt<<1|1]=rsum[rt<<1|1]=msum[rt<<1|1]=col[rt]?0:(m>>1);//情况与左儿子相同
       col[rt]=-1; //向下女更新过了,取消标记
    }
}
void pushup(int rt, int m)
{
    lsum[rt]=lsum[rt<<1]; //父区间的从左开始的最大空位等于左儿子从左开始的最大空位长度
    rsum[rt]=rsum[rt<<1|1];//右儿子情况与左儿子差不多
    if(lsum[rt]==(m-(m>>1))) lsum[rt]+=lsum[rt<<1|1];//若左儿子的最大左空间等于区间长度,则要加上右儿子的左空间,最为父区间最大左空间的长度
    if(rsum[rt]==(m>>1))   rsum[rt]+=rsum[rt<<1];//父区间的最大有空间与左儿子思想相同
    msum[rt]=max(max(msum[rt<<1],msum[rt<<1|1]),rsum[rt<<1]+lsum[rt<<1|1]);//然后更新父区间整个连续空间的最大值,可能左儿子空间的最大,也可能是右儿子,也可能是左右儿子连通,他们的空间和最大
}
void build(int l,int r,int rt)
{
    lsum[rt]=rsum[rt]=msum[rt]=r-l+1;//初始空间大小为区间长度
    col[rt]=-1;//表示没有被标记过
    if(l==r) return ;
    int m=(l+r)>>1;
    build(lson);
    build(rson);
}
int query(int p,int l,int r,int rt)
{
    if(l==r) return l;
    pushdown(rt,r-l+1); //向下更新子节点
    int m=(l+r)>>1;
    if(p<=msum[rt<<1]) return query(p,lson); //如果所需空间数比左儿子的空间数小,则住在左区间
    else if(rsum[rt<<1]+lsum[rt<<1|1]>=p) return m-rsum[rt<<1]+1;//若是比左儿子的右区间与右儿子左区间的和小,则直接返回左儿子右区间的最左端
    return query(p,rson);//否则在右儿子里找
}
void update(int L,int R,int c,int l,int r,int rt)
{
    if(L<=l&&r<=R)
    {
        msum[rt]=lsum[rt]=rsum[rt]=c?0:r-l+1; //c为1表示住人,0,表示走人,住人则和变为0,走人则赋为区间长度
        col[rt]=c;//改变区间的更新标志
        return ;
    }
    pushdown(rt,r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L,R,c,lson);//往左半部分找
    if(R>m)  update(L,R,c,rson);//往右半部分找
    pushup(rt,r-l+1);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        build(1,n,1);
        int op,a,b;
        while(m--)
        {
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d",&a);
                if(msum[1]<a) printf("0\n"); //若开始总区间的最大空位小于所需空位,直接输出0
                else
                {
                    int p = query(a,1,n,1);
                    printf("%d\n",p);
                    update(p,p+a-1,1,1,n,1);
                }
            }
            else
            {
                scanf("%d%d",&a,&b);
                update(a,a+b-1,0,1,n,1);
            }
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值