POJ3667 Hotel

题目大意

有n个本来为空的房间,有两种操作。
1:找到最左边连续长为l的一段,并占有它们。
2:将l到r区间置为空。

Solution

我们可以用线段树维护一段区间内最多连续的空房间数。但只记下这个答案,是无法把两个区间合并的。而只要多记下这个区间从左开始连续空房间数,从右开始连续空房间数,就能把两个区间合并了。

询问时,我们可以在左右子树中分别寻找答案。但还要多考虑一种答案在两棵子树中的情况,这个答案是由左子树从右开始连续空房间数,右子树从左开始连续空房间数贡献的。二分时多讨论这种情况就可以了。

代码
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int tag[1000010],tree[1000010],treel[1000010],treer[1000010];
void update(int t,int l,int r)
{
    tree[t]=max(treel[t<<1|1]+treer[t<<1],max(tree[t<<1],tree[t<<1|1]));
    treel[t]=treel[t<<1];
    int mid=(l+r)>>1;
    if (treel[t<<1]==mid-l+1) treel[t]+=treel[t<<1|1];
    treer[t]=treer[t<<1|1];
    if (treer[t<<1|1]==r-mid) treer[t]+=treer[t<<1];
}
void pushdown(int t,int l,int r)
{
    if (tag[t]==-1) return;
    int mid=(l+r)>>1; 
    if (tag[t]==0)
    {   
        treel[t<<1]=treer[t<<1]=tree[t<<1]=mid-l+1;
        tree[t<<1|1]=treel[t<<1|1]=treer[t<<1|1]=r-mid; 
        tag[t<<1]=tag[t<<1|1]=tag[t];   
    }
    else
    {

        treel[t<<1]=treer[t<<1]=tree[t<<1]=0;
        tree[t<<1|1]=treer[t<<1|1]=treel[t<<1|1]=0; 
        tag[t<<1]=tag[t<<1|1]=tag[t]; 
    }
    tag[t]=-1;
}
void build(int l,int r,int t)
{
    if (l==r)
    {
        tree[t]=treel[t]=treer[t]=1;
        tag[t]=-1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,t<<1);
    build(mid+1,r,t<<1|1);
    update(t,l,r);
    tag[t]=-1;
}
int query(int l,int r,int key,int t) 
{
    if (l==r) return l;
    pushdown(t,l,r);
    int mid=(l+r)>>1;
    if (tree[t<<1]>=key) return query(l,mid,key,t<<1);
    else if (treer[t<<1]+treel[t<<1|1]>=key) return mid-treer[t<<1]+1;
    else return query(mid+1,r,key,t<<1|1);
}
void modify(int l,int r,int x,int y,int key,int t)
{
    if (l==x&&y==r) 
    {
        tag[t]=key;
        if (key) treel[t]=treer[t]=tree[t]=0;
        else treel[t]=treer[t]=tree[t]=r-l+1;
        return; 
    }
    pushdown(t,l,r);
    int mid=(l+r)>>1;
    if (y<=mid) modify(l,mid,x,y,key,t<<1);
    else if (x>mid) modify(mid+1,r,x,y,key,t<<1|1);
    else modify(l,mid,x,mid,key,t<<1),modify(mid+1,r,mid+1,y,key,t<<1|1); 
    update(t,l,r);
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
        build(1,n,1);
        int l,r;
        while (m--)
        { 
            int k;
            scanf("%d",&k);
            if (k==1)
            {
                scanf("%d",&l);
                if (tree[1]>=l)
                {
                    int ans=query(1,n,l,1);
                    printf("%d\n",ans);
                    modify(1,n,ans,ans+l-1,1,1);
                }
                else puts("0");
            }
            else
            {
                scanf("%d%d",&l,&r);
                modify(1,n,l,l+r-1,0,1);
            }
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值