这道题的题目大意:
有一个区间[1,n],初始都是空的(你可以认为都是0),然后给出n个操作.
操作1格式为1,d,表示表示询问是否有一个r满足[r..r+d-1]都为空,如果有满足条件的,返回最小l,并把[l,l+d-1]全改为1.
操作2格式为2,l,r,表示把区间[l..r]全部赋值为空.
这道题是典型的线段树,我们需要维护几个信息:
1.这个区间的最长连续空区间sum,左端点开始往右的最长连续空区间lsum,以及右端点开始往左的最长连续空区间rsum.
2.区间左右端点l,r.
3.一个tag,等于0表示全部为空,等于1表示全部已填,等于-1表示有空也有填的.
我们就可以写出结构体:
struct tree{ int l,r; int sum,lsum,rsum; int tag; }tr[300001];
结构体写完之后建树,没啥好说的:
void build(int L,int R,int k=1){ tr[k].l=L;tr[k].r=R; tr[k].tag=0; tr[k].sum=tr[k].lsum=tr[k].rsum=R-L+1; if (L==R) return; int mid=(L+R)>>1; build(L,mid,k<<1); build(mid+1,R,k<<1|1); }
然后先写好pushup与pushdown.
pushup的操作很简单,就是分三种情况.
1.两个子节点tag都为0,那么此节点tag为0.
2.两个子节点tag都为1,那么此节点tag为1.
3.否则为-1.
那么就可以写了:
inline void pushup(int k){ int ls=k<<1,rs=k<<1|1; if (tr[ls].tag==0&&tr[rs].tag==0) tr[k].tag=0; else if (tr[ls].tag==1&&tr[rs].tag==1) tr[k].tag=1; else tr[k].tag=-1; if (tr[ls].tag==0) tr[k].lsum=tr[ls].sum+tr[rs].lsum; else tr[k].lsum=tr[ls].lsum; if (tr[rs].tag==0) tr[k].rsum=tr[rs].sum+tr[ls].rsum; else tr[k].rsum=tr[rs].rsum; tr[k].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].rsum+tr[rs].lsum); }
pushdown也比较简单,也分情况讨论:
1.tag=-1,直接return.
2.tag=0,往下传tag=0.
3.tag=1,往下传tag=1.
别的没什么了:
inline void pushdown(int k){ if (tr[k].tag==-1) return; int ls=k<<1,rs=ls|1; if (tr[k].tag==0){ tr[ls].tag=tr[rs].tag=0; tr[ls].sum=tr[ls].lsum=tr[ls].rsum=tr[ls].r-tr[ls].l+1; tr[rs].sum=tr[rs].lsum=tr[rs].rsum=tr[rs].r-tr[rs].l+1; }else { tr[ls].tag=tr[rs].tag=1; tr[ls].sum=tr[ls].lsum=tr[ls].rsum=tr[rs].sum=tr[rs].lsum=tr[rs].rsum=0; } }
那么就来看询问.
首先我们判断是否有需要的连续空长度L在线段树中.
直接判断L与整棵树的最长空位长度tr[1].sum的大小即可.
也就是说:
inline bool can(int L){ if (L<=tr[1].sum) return true; else return false; }
那么我们继续讨论有足够空位的情况.
我们假设我们已经找到了节点k是有足够空位的.
那么我们先看左儿子是否足够,再看中间的是否足够,最后看有儿子是否足够.
值得注意的是,由于中间空位的长度是tr[ls].rsum+tr[rs].lsum,所以若tr[ls].rsum=0,就不用判中间的那段了.
因为若tr[ls].rsum=0,说明中间那段整段都是在右儿子中的.
同理,tr[rs].lsum=0,也不用判了.
若是左边那段,跑左儿子,若是右边那段,跑右儿子.
若已经是叶子节点了,就直接返回tr[k].l或tr[k].r.
然后最重要的就是是中间那段.
这个时候你已经知道最左边的满足的点在左儿子.
所以直接返回tr[ls].r-tr[ls].lsum+1就可以了.
当然有一个没有右儿子的情况,那你就当叶子节点处理,返回tr[k].l或tr[k].r.
但那不存在.
这样就判好了.
其实最后一个判断叶子是一个对L=1的特判.
代码如下(query表示左端点):
int query(int L,int k=1){ if (tr[k].r==tr[k].l) return tr[k].r; pushdown(k); int ls=k<<1,rs=k<<1|1; if (L<=tr[ls].sum) return query(L,ls); if (tr[ls].rsum&&tr[rs].lsum&&L<=tr[ls].rsum+tr[rs].lsum) return tr[ls].r-tr[ls].rsum+1; if (L<=tr[rs].sum) return query(L,rs); }
所以接下来是两个修改.
先讨论改1.
改1的好像很简单,注意pushup与pushdown都写.
那么就是这样:
void change1(int L,int R,int k=1){ if (L==tr[k].l&&R==tr[k].r){ tr[k].tag=1; tr[k].sum=tr[k].lsum=tr[k].rsum=0; return; } pushdown(k); int mid=(tr[k].l+tr[k].r)>>1; if (R<=mid) change1(L,R,k<<1); else if (L>mid) change1(L,R,k<<1|1); else change1(L,mid,k<<1),change1(mid+1,R,k<<1|1); pushup(k); }
所以change0同理:
void change0(int L,int R,int k=1){ if (L==tr[k].l&&R==tr[k].r){ tr[k].tag=0; tr[k].sum=tr[k].lsum=tr[k].rsum=tr[k].r-tr[k].l+1; return; } pushdown(k); int mid=(tr[k].l+tr[k].r)>>1; if (R<=mid) change0(L,R,k<<1); else if (L>mid) change0(L,R,k<<1|1); else change0(L,mid,k<<1),change0(mid+1,R,k<<1|1); pushup(k); }
那么这道题就可以写了.
模拟题目的意思,用到操作的时候用就可以了.
完整代码如下:
#include<bits/stdc++.h> using namespace std; #define rep(i,j,k) for (int i=j;i<=k;i++) struct tree{ int l,r; int sum,lsum,rsum; int tag; }tr[300001]; void build(int L,int R,int k=1){ tr[k].l=L;tr[k].r=R; tr[k].tag=0; tr[k].sum=tr[k].lsum=tr[k].rsum=R-L+1; if (L==R) return; int mid=(L+R)>>1; build(L,mid,k<<1); build(mid+1,R,k<<1|1); } inline void pushup(int k){ int ls=k<<1,rs=k<<1|1; if (tr[ls].tag==0&&tr[rs].tag==0) tr[k].tag=0; else if (tr[ls].tag==1&&tr[rs].tag==1) tr[k].tag=1; else tr[k].tag=-1; if (tr[ls].tag==0) tr[k].lsum=tr[ls].sum+tr[rs].lsum; else tr[k].lsum=tr[ls].lsum; if (tr[rs].tag==0) tr[k].rsum=tr[rs].sum+tr[ls].rsum; else tr[k].rsum=tr[rs].rsum; tr[k].sum=max(max(tr[ls].sum,tr[rs].sum),tr[ls].rsum+tr[rs].lsum); } inline void pushdown(int k){ if (tr[k].tag==-1) return; int ls=k<<1,rs=ls|1; if (tr[k].tag==0){ tr[ls].tag=tr[rs].tag=0; tr[ls].sum=tr[ls].lsum=tr[ls].rsum=tr[ls].r-tr[ls].l+1; tr[rs].sum=tr[rs].lsum=tr[rs].rsum=tr[rs].r-tr[rs].l+1; }else { tr[ls].tag=tr[rs].tag=1; tr[ls].sum=tr[ls].lsum=tr[ls].rsum=tr[rs].sum=tr[rs].lsum=tr[rs].rsum=0; } } inline bool can(int L){ if (L<=tr[1].sum) return true; else return false; } int query(int L,int k=1){ if (tr[k].r==tr[k].l) return tr[k].r; pushdown(k); int ls=k<<1,rs=k<<1|1; if (L<=tr[ls].sum) return query(L,ls); if (tr[ls].rsum&&tr[rs].lsum&&L<=tr[ls].rsum+tr[rs].lsum) return tr[ls].r-tr[ls].rsum+1; if (L<=tr[rs].sum) return query(L,rs); } void change1(int L,int R,int k=1){ if (L==tr[k].l&&R==tr[k].r){ tr[k].tag=1; tr[k].sum=tr[k].lsum=tr[k].rsum=0; return; } pushdown(k); int mid=(tr[k].l+tr[k].r)>>1; if (R<=mid) change1(L,R,k<<1); else if (L>mid) change1(L,R,k<<1|1); else change1(L,mid,k<<1),change1(mid+1,R,k<<1|1); pushup(k); } void change0(int L,int R,int k=1){ if (L==tr[k].l&&R==tr[k].r){ tr[k].tag=0; tr[k].sum=tr[k].lsum=tr[k].rsum=tr[k].r-tr[k].l+1; return; } pushdown(k); int mid=(tr[k].l+tr[k].r)>>1; if (R<=mid) change0(L,R,k<<1); else if (L>mid) change0(L,R,k<<1|1); else change0(L,mid,k<<1),change0(mid+1,R,k<<1|1); pushup(k); } int n,m; inline void into(){ int opt,l,r; scanf("%d%d",&n,&m); build(1,n); rep(i,1,m){ scanf("%d",&opt); if (opt==1) { scanf("%d",&l); if (can(l)) { r=query(l); printf("%d\n",r); change1(r,r+l-1); }else printf("0\n"); }else { scanf("%d%d",&l,&r); change0(l,l+r-1); } } } int main(){ into(); return 0; }