题意:有一开始全是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;
}