P2894 [USACO08FEB]Hotel G(维护最长连续的1)

P2894 [USACO08FEB]Hotel G(维护最长连续的1)

传送门

题意:

给你长度为n的1串,m次操作

  1. 找到最左边长度为x连续的1,若找到区间修改为0,找不到,输出0
  2. 将l-r区间修改为1

思路:

对于1操作,由于左端点不确定,所以我们用线段树维护区间 [ l , r ] [l,r] [l,r]最长的连续的1

即线段树维护

  1. len区间最长连续空房的长度
  2. lmx从l端点开始最长连续空房的长度
  3. rmx从r端点开始最长连续空房的长度

之后在线段树上二分,即可完成1操作。

#include <iostream>
#define lch (k<<1)
#define rch (k<<1|1)
#define mid (l+r>>1)
using namespace std;
const int N=1e5+7;
int n,m;
int len[4*N],sum[4*N],lmx[4*N],rmx[4*N],lz[4*N];
//len区间最长连续空房的长度
//lmx从l端点开始最长连续空房的长度
//rmx从r端点开始最长连续空房的长度
//lazy为1表示退房,为2表示开房
//区间长度,记录后方便计算
void init(int k,int l,int r){
    sum[k]=len[k]=lmx[k]=rmx[k]=r-l+1;
    lz[k]=-1;
    if(l==r){
        return;
    }
    init(lch,l,mid);
    init(rch,mid+1,r);
}
void pushup(int k){
    lmx[k]=(lmx[lch]==len[lch])? lmx[lch]+lmx[rch]:lmx[lch];
    //若左儿子区间全空那么lmax可以横跨左右儿子,否则不能
    rmx[k]=(rmx[rch]==len[rch])? rmx[rch]+rmx[lch]:rmx[rch];
    //若右儿子区间全空那么rmax可以横跨左右儿子,否则不能
    sum[k]=max(rmx[lch]+lmx[rch],max(sum[lch],sum[rch]));
    //有三种情况,sum全在左儿子,全在右儿子,横跨左右儿子
}

void pushdown(int k){
    if(lz[k]!=-1){
        //下传lazy标记
        lz[lch]=lz[rch]=lz[k];
        sum[lch]=lmx[lch]=rmx[lch]=len[lch]*lz[k];
        sum[rch]=lmx[rch]=rmx[rch]=len[rch]*lz[k];
        lz[k]=-1;
    }
}
//区间赋值0/1
void update(int k,int l,int r,int ql,int qr,int val){
    if(ql<=l&&r<=qr){

        lmx[k]=rmx[k]=sum[k]=len[k]*val;
        lz[k]=val;
        return;
    }
    pushdown(k);
    if(ql<=mid)   update(lch,l,mid,ql,qr,val);
    if(mid+1<=qr) update(rch,mid+1,r,ql,qr,val);
    pushup(k);
}
//寻找最左边大于等于x的线段
int query(int k,int l,int r,int x){
    if(l==r) return l;
    pushdown(k);
    if(sum[lch]>=x) return query(lch,l,mid,x);
    //递归到左儿子
    else if(rmx[lch]+lmx[rch]>=x) return mid-rmx[lch]+1;
    //左右儿子合并后满足就用中间
    else return query(rch,mid+1,r,x);
    //递归到右儿子
}
int ask(int k,int l,int r,int p){
    if(l==r) return sum[k];
    pushdown(k);
    if(p<=mid) return ask(lch,l,mid,p);
    else return ask(rch,mid+1,r,p);
}
int main(){
    cin>>n>>m;
    init(1,1,n);
    while(m--){
        int op,x,y;
        cin>>op;
        if(op==1){
            cin>>x;
            if(sum[1]>=x){
                int pos=query(1,1,n,x);
                cout<<pos<<"\n";
                update(1,1,n,pos,pos+x-1,0);
            }else{
                cout<<"0\n";
            }
        }else{
            cin>>x>>y;
            update(1,1,n,x,x+y-1,1);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值