POJ 3667 线段树 + 延迟标记 + 区间处理

/*

贴个神牛的代码,自己写的代码难看的别提有多恶心了 ...

类型:线段树 + 延迟标记 + 区间合并... 

*/
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<algorithm>
using namespace std;
#define L(r) r<<1
#define R(r) r<<1|1
const int MAXM=50005;
typedef struct
{
    int lsum,rsum,msum;//分别表示左边最大房间数,右边最大房间数,整体最大房间数
    int cover;//是否住下
}node;
node tree[MAXM<<2];
void pushDown(int root,int m)//向下更新
{
    if(tree[root].cover==-1) return;//是否已经更新
    tree[L(root)].cover=tree[R(root)].cover=tree[root].cover;
    tree[L(root)].msum=tree[L(root)].lsum=tree[L(root)].rsum=tree[root].cover?0:m-(m>>1);
    tree[R(root)].msum=tree[R(root)].lsum=tree[R(root)].rsum=tree[root].cover?0:(m>>1);
    tree[root].cover=-1;
}
void pushUp(int root,int m)//向上更新
{
    tree[root].lsum=tree[L(root)].lsum;
    tree[root].rsum=tree[R(root)].rsum;
    if(tree[root].lsum==m-(m>>1) ) tree[root].lsum+=tree[R(root)].lsum;  // 区间合并 
    if(tree[root].rsum==(m>>1) ) tree[root].rsum+=tree[L(root)].rsum;   
    tree[root].msum=max(tree[R(root)].lsum+tree[L(root)].rsum,max(tree[L(root)].msum,tree[R(root)].msum) );
}
void Create(int l,int r,int root)//建树过程
{
    tree[root].cover=-1;
    tree[root].lsum=tree[root].rsum=tree[root].msum=r-l+1;
    if(l==r){return;}
    int mid=(l+r)>>1;
    Create(l,mid,L(root));
    Create(mid+1,r,R(root));
}
void update(int ll,int rr,int c,int l,int r,int root)//更新
{
    if(ll<=l&&r<=rr)//如果找到这个范围就直接赋值返回,不向下继续更新
    {
        tree[root].msum=tree[root].lsum=tree[root].rsum=c?0:r-l+1;
        tree[root].cover=c;
        return;
    }
    pushDown(root,r-l+1);//用到这个节点的子节点的时候就向下更新
    int m=(l+r)>>1;
    if(ll<=m) update(ll,rr,c,l,m,L(root));
    if(m<rr) update(ll,rr,c,m+1,r,R(root));
    pushUp(root,r-l+1);//向下更新完后需要把节点信息反馈给父节点
}
int query(int w,int l,int r,int root)//查询满足连续房间数量的最左节点
{
    if(l==r) return l;
    pushDown(root,r-l+1 );//用到这个节点的子节点的时候就向下更新
    int m=(l+r)>>1;
     注意顺序, 区间的处理 
    if(tree[L(root)].msum>=w) return query(w,l,m,L(root));//如果左儿子满足就询问左儿子
    else if(tree[L(root)].rsum+tree[R(root)].lsum>=w) return m-tree[L(root)].rsum+1;//如果左儿子和右儿子之间的数量满足,则范围左儿子右边连续房间第一个的编号
    else return query(w,m+1,r,R(root));//如果两者都不满足则询问右儿子
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        Create(1,n,1);
        int a,num,x,d,p;
        for(int i=0;i<m;++i)
        {
            scanf("%d",&a);
            if(1==a)
            {
                scanf("%d",&num);
                if(tree[1].msum<num) puts("0");//如果整个区间的最大连续房间数量小于预定的数量就输出0
                else
                {
                    p=query(num,1,n,1);//找到最左节点p
                    printf("%d\n",p);
                    update(p,p+num-1,1,1,n,1);//把已经有人的房间标记一下
                }
            }
            else
            {
                scanf("%d %d",&x,&d);
                update(x,x+d-1,0,1,n,1);//把此范围的房间标记成无人
            }
        }
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值