POJ 3667 Hotel

POJ 3667 Hotel

线段树

题意

设计数据结构,维护一段序列,初始全1,支持2种操作:

  • 将一段区间置1
  • 从头查找第一段长度不小于k的序列,并将这k个位置置0

思路

线段树维护序列。维护前缀1(pre),后缀1(suf),区间最大连续1(sum)。更新时取左右中长度大的更新。
找不小于k时有坑,必须按照下面步骤一步步来:

  1. 如果跟节点的区间最大连续1小于k,那么无解
  2. 如果左子区间sum大于等于k,递归左子区间
  3. 如果左子区间suf+右子区间pre大于等于k,输出mid-stree[rt<<1].suf+1!!这一步不能用当前区间sum和lpos rpos代替,因为最大sum有可能出现在右子区间。
  4. 递归右子区间

别忘了递归基,l==r时返回l。我在这wa了好久。

代码

感觉挺考验编程能力的

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define M(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=100007;
const int oo=0x3f3f3f3f;
struct Stree
{
    int sum;int pre, suf;int set1;int set0;
    //int lpos, rpos;
}stree[MAXN<<2];
int max3(int a, int b, int c) { return std::max(std::max(a, b), c); }
void pushup(int l, int r, int rt)
{
    int ll=stree[rt<<1].sum, rr=stree[rt<<1|1].sum, mm=stree[rt<<1].suf+stree[rt<<1|1].pre;

    if(ll>=rr&&ll>=mm)
        stree[rt].sum=ll;
    else if(rr>ll&&rr>mm)
        stree[rt].sum=rr;
    else
        stree[rt].sum=mm;

    int mid=(l+r)>>1;
    if(stree[rt<<1].pre==mid-l+1)
        stree[rt].pre=stree[rt<<1].pre+stree[rt<<1|1].pre;
    else stree[rt].pre=stree[rt<<1].pre;

    if(stree[rt<<1|1].suf==r-mid)
        stree[rt].suf=stree[rt<<1].suf+stree[rt<<1|1].suf;
    else stree[rt].suf=stree[rt<<1|1].suf;
}
void pushdown(int l, int r, int rt)
{
    int mid=(l+r)>>1;
    if(stree[rt].set0)
    {
        stree[rt<<1].set0=stree[rt<<1|1].set0=1;
        stree[rt<<1].set1=stree[rt<<1|1].set1=0;
        stree[rt<<1].sum=stree[rt<<1].pre=stree[rt<<1].suf=0;
        stree[rt<<1|1].sum=stree[rt<<1|1].pre=stree[rt<<1|1].suf=0;

        stree[rt].set0=0;
    }
    if(stree[rt].set1)
    {
        stree[rt<<1].set0=stree[rt<<1|1].set0=0;
        stree[rt<<1].set1=stree[rt<<1|1].set1=1;
        stree[rt<<1].sum=stree[rt<<1].pre=stree[rt<<1].suf=mid-l+1;
        stree[rt<<1|1].sum=stree[rt<<1|1].pre=stree[rt<<1|1].suf=r-mid;

        stree[rt].set1=0;
    }
}
void build(int l, int r, int rt)
{
    stree[rt].pre=stree[rt].set1=stree[rt].suf=stree[rt].sum=0;
    if(l==r) { stree[rt].pre=stree[rt].suf=stree[rt].sum=1; return; }
    int mid=(l+r)>>1;
    build(lson), build(rson);
    pushup(l, r, rt);
}
void update(int L, int R, int v, int l, int r, int rt)
{
    if(L<=l&&r<=R)
    {
        if(v==1)
        {
            stree[rt].set1=1;stree[rt].sum=r-l+1;stree[rt].set0=0;
            stree[rt].pre=stree[rt].suf=r-l+1;
        }
        else
        {
            stree[rt].set0=1;stree[rt].sum=0;stree[rt].set1=0;
            stree[rt].pre=stree[rt].suf=0;
        }
        return;
    }
    pushdown(l, r, rt);
    int mid=(l+r)>>1;
    if(L<=mid) update(L, R, v, lson);
    if(R>mid) update(L, R, v, rson);
    pushup(l, r, rt);
}
int query(int am, int l, int r, int rt)
{
    if(l==r) return l;
    pushdown(l, r, rt);
    int mid=(l+r)>>1;
    if(am<=stree[rt<<1].sum)
        return query(am, lson);
    else if(am<=stree[rt<<1].suf+stree[rt<<1|1].pre)
        return mid-stree[rt<<1].suf+1;
    else if(am<=stree[rt<<1|1].sum)
        return query(am, rson);
}
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m)==2)
    {
        build(1, n, 1);
        for(int i=0;i<m;i++)
        {
            int op;scanf("%d", &op);
            if(op==1)
            {
                int len;scanf("%d", &len);int res=0;
                if(stree[1].sum<len) res=0;
                else
                {
                    res=query(len, 1, n, 1);
                    update(res, res+len-1, 0, 1, n, 1);
                }
                printf("%d\n", res);
            }
            else if(op==2)
            {
                int x, y;scanf("%d%d", &x, &y);
                update(x, x+y-1, 1, 1, n, 1);
            }
        }
    }
    //system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值