题目链接如下所示:
这题的意思其实是用线段树维护一个连续区间和。
我们用lsum和rsum代表节点的最左连续区间和,最右区间连续区间和。
那么在push_up的时候我们要做的就是根据孩子节点的区间和更新当前根节点的区间和。如果是等于了左右区间的话,说明是可以扩展的区间和,也就是可以继续扩大。
对于D操作,我们简单update对应位置的值变成0。
对于R操作,我们将上一个update的位置的值变成1。
对于Q操作,我们的目标是找到对应位置的最长连续区间和。假设当前的点代表的区间是[l,r],要询问的位置是pos,那么当前左右区间长度我们设为pos-l+1和r-pos+1,若lsum或者rsum大于等于这个区间的长度,我们就判断找到对应区间的一部分,返回对应连续区间和。由于这个区间肯定是可扩展的,所以我们直接返回的那一次结果还要加上另外一个区间的邻接的连续区间和。并且在这之后的返回过程中不需要更新,因为如果还存在可以相连的连续区间,递归返回的层数和这个就不一样了,会产生矛盾。
代码如下所示:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<vector>
#include<algorithm>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<cmath>
#include<climits>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int MAXN=50001;
int lsum[MAXN<<2],rsum[MAXN<<2];
int flag;
void push_up(int rt,int m){
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
if (lsum[rt]==m-(m>>1)) lsum[rt]+=lsum[rt<<1|1];
if (rsum[rt]==m>>1) rsum[rt]+=rsum[rt<<1];
}
void build(int l,int r,int rt){
if (l==r){
lsum[rt]=rsum[rt]=1;
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
push_up(rt, r-l+1);
}
void update(int pos,int val,int l,int r,int rt){
if (l==r){
lsum[rt]=rsum[rt]=val;
return;
}
int mid=(l+r)>>1;
if (pos<=mid){
update(pos,val,lson);
} else{
update(pos,val,rson);
}
push_up(rt,r-l+1);
}
int query(int pos,int l,int r,int rt){
if (rsum[rt]>=r-pos+1){
flag=1;
return rsum[rt];
}
if (lsum[rt]>=pos-l+1){
flag=1;
return lsum[rt];
}
if (l==r) return 0;
int res;
int mid=(l+r)>>1;
if (pos<=mid){
res=query(pos,lson);
if (flag){
flag=0;
res+=lsum[rt<<1|1];
}
} else{
res=query(pos,rson);
if (flag){
flag=0;
res+=rsum[rt<<1];
}
}
return res;
}
int main(){
int n,m,pos;
while (~scanf("%d %d",&n,&m)){
stack<int> st;
build(1,n,1);
char op[2];
for (int i = 0; i < m; ++i) {
scanf("%s",op);
if (op[0]=='D'){
scanf("%d",&pos);
st.push(pos);
update(pos,0,1,n,1);
}else if (op[0]=='Q'){
scanf("%d",&pos);
flag=0;
printf("%d\n", query(pos,1,n,1));
}else{
update(st.top(),1,1,n,1);
st.pop();
}
}
}
return 0;
}