poj 3667 hotel

本文介绍了一种解决区间最大子段和问题的方法,通过使用线段树进行区间更新和查询,实现了对连续区间内元素的操作,如退房和住房。文章详细解释了如何维护线段树节点信息,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:Hotel POJ - 3667

题意:安排住房,n个连续的房间编号为1~n,m次操作,一种是安排连续的k个房间,一种是从房间i开始,退房k个。

如果我们将没有人住的房间标为1,有人住的标为0,那么就可以将这个问题转化为求区间最大子段和。

于是延迟标记可以设置为两种,退房时将标记设为1,住房时设为0。

因为安排房间的是你,所以为了不必要的麻烦,我们尽量将房间开的靠前一点。想一想如果随便开,比如有10个房间,先开8个占了2~8,如果再开两个的话就开不了了。

维护四个东西,区间和,区间紧靠左端点的最大值,紧靠右端点的最大值,区间最大子段和。

维护这四个东西可以参考:区间最大子段和

与区间最大子段和稍有不同的是合并两个区间的时候,维护紧靠区间左端点与右端点的最大值与之有所不同,请看代码。

查询的时候注意一下,类似于求第k大的思想,假设要安排x个连续房间,

  1. 如果左子区间的最大子段和>=x,那么去左子树查
  2. 如果两段子区间合并>=x,那么就可以返回答案了
  3. 如果右子区间的最大子段和>=x,那么就查右子树
  4. 如果都不行,则说明开不了房间。
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int maxn=5e4+7;

struct Node{
    int sum;
    int lmax;
    int rmax;
    int dat;
}a[maxn<<2|1];

int lazy[maxn<<2|1];

void pushup(int l,int r,int k){
    int mid=(l+r)>>1;
    a[k].sum=a[k<<1].sum+a[k<<1|1].sum;
    
    a[k].lmax=a[k<<1].lmax;
    //如果要取右子树,为了保证紧靠左端点,左子树必须取所有元素才行,为了保证房间连续,我们必须加入判断房子个数语句;
    if(a[k<<1].sum==mid-l+1) a[k].lmax=max(a[k].lmax,a[k<<1].sum+a[k<<1|1].lmax);
    
    a[k].rmax=a[k<<1|1].rmax;
    if(a[k<<1|1].sum==r-mid) a[k].rmax=max(a[k].rmax,a[k<<1|1].sum+a[k<<1].rmax);

    a[k].dat=max(max(a[k<<1].dat,a[k<<1|1].dat),a[k<<1].rmax+a[k<<1|1].lmax);
}

void pushdown(int l,int r,int k){
    int mid=(l+r)>>1;
    if(lazy[k]!=-1){
        lazy[k<<1]=lazy[k<<1|1]=lazy[k];
        if(lazy[k]==1){
            a[k<<1].sum=a[k<<1].lmax=a[k<<1].rmax=a[k<<1].dat=(mid-l+1);
            a[k<<1|1].sum=a[k<<1|1].lmax=a[k<<1|1].rmax=a[k<<1|1].dat=r-mid;
        }
        else{
            a[k<<1].sum=a[k<<1].lmax=a[k<<1].rmax=a[k<<1].dat=a[k<<1|1].sum=a[k<<1|1].lmax=a[k<<1|1].rmax=a[k<<1|1].dat=lazy[k];
        }
        lazy[k]=-1;
    }
}

void build(int l,int r,int k){
    lazy[k]=-1;
    if(l==r){
        a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    pushup(l,r,k);
}

void updata(int l,int r,int k,int L,int R,int val){
    if(l>=L&&r<=R){
        if(val==1){
            a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=r-l+1;
        }
        else{
            a[k].sum=a[k].lmax=a[k].rmax=a[k].dat=val;
        }
        lazy[k]=val;
        return ;
    }
    pushdown(l,r,k);
    int mid=(l+r)>>1;
    if(L<=mid) updata(l,mid,k<<1,L,R,val);
    if(R>mid) updata(mid+1,r,k<<1|1,L,R,val);
    pushup(l,r,k);
}

int n;
int myfind(int l,int r,int k,int x){
    if(l==r){
        if(a[k].sum==1&&x==1) return l;
        return -1;
    }
    pushdown(l,r,k);
    //if(a[k].dat<x) return -1;
    int mid=(l+r)>>1;

    int lz=a[k<<1].dat,rz=a[k<<1|1].dat;
    if(lz>=x) return myfind(l,mid,k<<1,x);
    else if(a[k<<1].rmax+a[k<<1|1].lmax>=x) return mid-a[k<<1].rmax+1;
    else if(rz>=x) return myfind(mid+1,r,k<<1|1,x);
    return -1;


}



int main(){
    int m;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    int id,l,r;
    while(m--){
        scanf("%d%d",&id,&l);
        if(id==1){
            int res=myfind(1,n,1,l);
            if(res==-1) printf("0\n");
            else{
                printf("%d\n",res);
                updata(1,n,1,res,res+l-1,0);
            }
        }
        else{
            scanf("%d",&r);
            updata(1,n,1,l,l+r-1,1);
        }
    }


}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值