poj 3667 Hotel //线段树区间+区间合并 ※※

心得:线段树主要需要思考的是

1.维护什么:每一个节点需要维护的东西

2.pushUp:两个小区间合并成大区间的方法合理性

3.pushDown:lazy向下传递,以及节点的各属性更新

4.query:花式query

题意:n间房子 ,操作1:问你能否找出连续的x个房子, 如果能输出最左边的房间编号,并入住。操作2:让连续的区间住户退房。

思路:线段树节点维护 最大连续空闲区间长度mx最左边的连续空闲区间长度lto最右边的连续空闲区间长度rto,整个区间是否是空的a,以及lazy。

pushUp:考虑左边右边以及连接处。

pushDown:lz标记为-1代表不需要操作,0代表让所有房价退房,1代表入住,然后向下更新小区间信息。

一个线段树就是一下午

#include  <map>
#include  <set>
#include  <cmath>
#include  <queue>
#include  <cstdio>
#include  <vector>
#include  <climits>
#include  <cstring>
#include  <cstdlib>
#include  <iostream>
#include  <algorithm>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define PII pair<int,int>
#define MP make_pair
const LL mod = 1e9+7;
const int MX = 4e5+5;

int lto[MX],rto[MX],mx[MX],a[MX],lz[MX];//a标记这一段有满或空,lz标记(-1初始化

inline void pushUp(int rt){
    lto[rt]=lto[rt<<1];
    if(a[rt<<1]==0) lto[rt]+=lto[rt<<1|1];
    rto[rt]=rto[rt<<1|1];
    if(a[rt<<1|1]==0) rto[rt]+=rto[rt<<1];
    mx[rt]=max(lto[rt<<1|1]+rto[rt<<1],max(mx[rt<<1],mx[rt<<1|1]));
    a[rt]=1;
    if(a[rt<<1]==0&&a[rt<<1|1]==0) a[rt]=0;
}
inline void pushDown(int rt,int le){
    if(lz[rt]==-1)return ;
    if(lz[rt]==1){
        mx[rt]=0,mx[rt<<1]=0,mx[rt<<1|1]=0;
        lto[rt]=0,lto[rt<<1]=0,lto[rt<<1|1]=0;
        rto[rt]=0,rto[rt<<1]=0,rto[rt<<1|1]=0;
        a[rt]=1,a[rt<<1]=1,a[rt<<1|1]=1;
    }
    if(lz[rt]==0){
        mx[rt]=le,mx[rt<<1]=le-(le>>1),mx[rt<<1|1]=le>>1;
        lto[rt]=le,lto[rt<<1]=le-(le>>1),lto[rt<<1|1]=le>>1;
        rto[rt]=le,rto[rt<<1]=le-(le>>1),rto[rt<<1|1]=le>>1;
        a[rt]=0,a[rt<<1]=0,a[rt<<1|1]=0;
    }
    lz[rt<<1]=lz[rt<<1|1]=lz[rt];
    lz[rt]=-1;
}

void build(int l,int r,int rt){
    lz[rt]=-1;
    if(l==r){
        lto[rt]=rto[rt]=mx[rt]=1;a[rt]=0;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushUp(rt);
}

void update(int L,int R,int c,int l,int r,int rt){
    if(l>=L&&r<=R){
        lz[rt]=c;
        mx[rt]=lto[rt]=rto[rt]=(c^1)*(r-l+1);
        a[rt]=c;
        return ;
    }
    pushDown(rt,r-l+1);
    int m=(l+r)>>1;
    if(m>=L) update(L,R,c,l,m,rt<<1);
    if(m<R)  update(L,R,c,m+1,r,rt<<1|1);
    pushUp(rt);
}

int query(int k,int l,int r,int rt){ //k个连续
    if(l==r) {
        if(mx[rt]<k) return 0;
        return l;
    }
    pushDown(rt,r-l+1);
    int m=(l+r)>>1;
    if(mx[rt<<1]>=k) return query(k,l,m,rt<<1);
    if(rto[rt<<1]+lto[rt<<1|1]>=k) return m-rto[rt<<1]+1;
    return query(k,m+1,r,rt<<1|1);

}

int main(){
    int n,m;cin>>n>>m;
    build(1,n,1);
    while(m--){
        int op;scanf("%d",&op);
        if(op==1){
            int x;scanf("%d",&x);
            int l=query(x,1,n,1);
            printf("%d\n",l);
            if(l==0)continue;
            update(l,l+x-1,1,1,n,1);
        }
        else {
            int l,x;scanf("%d%d",&l,&x);
            update(l,l+x-1,0,1,n,1);
        }
    }
    return 0;
}
/*
10 9
1 3
1 3
1 4
1 1*/

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值