[BZOJ1593][Usaco2008 Feb]Hotel 旅馆 线段树

链接

题意:有一开始全是0的序列,实现2个操作

1.找区间[l,r]内最靠前的连续x个0,并把它变成1

2.把区间[l,r]变成0

题解

先维护一下0个数的最大子段和,然后考虑操作1如何实现

在线段树上询问到o这个节点时,设ls为左儿子,rs为右儿子,

\(t[ls].tmax\ge x\) 说明答案在完全在左边,向左递归

否则若 \(t[ls].rmax+t[rs].lmax\ge x\) 则说明它横跨左右区间,直接返回 \(mid-t[ls].rmax+1\)

否则完全在右边,向右边递归

#include<bits/stdc++.h>
#define REP(i,a,b) for(int i(a);i<=(b);++i)
using namespace std;
typedef long long ll;
inline int read(){char c;int w;
    while(!isdigit(c=getchar()));w=c&15;
    while(isdigit(c=getchar()))w=w*10+(c&15);return w;
}
inline char smax(int&x,const int&y){return x<y?x=y,1:0;}
inline char smin(int&x,const int&y){return x>y?x=y,1:0;}
const int N=50005;
struct node{
    int lmax,rmax,tmax,len,tag;
}t[N<<2];
#define ls o<<1
#define rs o<<1|1
inline void change(int o,int w){
    if(!o)return;int&x=t[o].len;
    if(!w)t[o]=(node){x,x,x,x,w};
    else t[o]=(node){0,0,0,x,w};
}
inline void build(int o,int l,int r){
    t[o].len=r-l+1,change(o,0),t[o].tag=-1;
    if(l==r)return;int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
}
inline void pushdown(int o){if(~t[o].tag)change(ls,t[o].tag),change(rs,t[o].tag),t[o].tag=-1;}
inline void pushup(int o){
    t[o].lmax=t[ls].lmax==t[ls].len?t[ls].lmax+t[rs].lmax:t[ls].lmax;
    t[o].rmax=t[rs].rmax==t[rs].len?t[ls].rmax+t[rs].rmax:t[rs].rmax;
    t[o].tmax=max(t[ls].tmax,t[rs].tmax);
    smax(t[o].tmax,t[ls].rmax+t[rs].lmax);
}
inline void update(int o,int l,int r,int x,int y,int z){
    if(x<=l&&r<=y)return change(o,z);
    int mid=l+r>>1;pushdown(o);
    if(x<=mid)update(ls,l,mid,x,y,z);
    if(y>mid)update(rs,mid+1,r,x,y,z);
    pushup(o);
}
inline int ask(int o,int l,int r,int x){
    if(l==r)return l;int mid=l+r>>1;pushdown(o);
    if(t[ls].tmax>=x)return ask(ls,l,mid,x);
    else if(t[ls].rmax+t[rs].lmax>=x)return mid-t[ls].rmax+1;
    return ask(rs,mid+1,r,x);
}
int main(){
    int n=read(),q=read();
    #define all 1,1,n
    build(all);
    while(q--){
        int op=read(),l=read(),p;
        if(op==1){
            if(t[1].tmax<l){puts("0");continue;}
            printf("%d\n",p=ask(all,l));
            update(all,p,l+p-1,1);
        }
        else update(all,l,l+read()-1,0);
    }
    return 0;
}

转载于:https://www.cnblogs.com/HolyK/p/9884631.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值