题意:
n个点,m个操作(1<=n,m<=5e4),m行中每行一个字符c表示操作类型,一个整型数x表示第x个点
D x 表示去掉第x点,Q x表示需输出含 x 的最大连续区间的长度,R x表示还原最后去掉的点
思路:
第一种思路:利用线段树求解, 线段树维护一个最长连续子段和,然后查询的时候判断x点是在左边区间还是右边区间,如果在左边区间就判断左边区间的rmax(包含右断点的最大连续子段和)的长度是否超过了x那个点,如果超过了那么直接返回 左边区间.rl + 右边区间.ll。 否则继续查询左边区间。x在右边区间同理。
code:
#include <bits/stdc++.h>
using namespace std;
struct node
{
int ll,rl,ml;
}tree[50005*4];
int n,m,x;
char c;
stack<int> sk;
void build(int l,int r,int rt)
{
tree[rt].ll=tree[rt].rl=tree[rt].ml=r-l+1;
if(l==r) return;
build(l,(l+r)/2,rt<<1);
build((l+r)/2+1,r,rt<<1|1);
}
void update(int x,int c,int l,int r,int rt)
{
if(x<l||x>r) return;
if(l==r)
{
tree[rt].ll=tree[rt].rl=tree[rt].ml=c;
return;
}
update(x,c,l,(l+r)/2,rt<<1);
update(x,c,(l+r)/2+1,r,rt<<1|1);
//最大连续长度是 1.左儿子最大长 2.右儿子最大长 3.左儿子右边界+右儿子左边界最大长 之一
tree[rt].ml=max(max(tree[rt<<1].ml,tree[rt<<1|1].ml),tree[rt<<1].rl+tree[rt<<1|1].ll);
tree[rt].ll=tree[rt<<1].ll;//最大左长为左儿子左长,若左儿子左长满区间,则要加右儿子左长
if(tree[rt].ll==(l+r)/2-l+1) tree[rt].ll+=tree[rt<<1|1].ll;
tree[rt].rl=tree[rt<<1|1].rl;//最大右长同理
if(tree[rt].rl==r-(l+r)/2) tree[rt].rl+=tree[rt<<1].rl;
}
int query(int x,int l,int r,int rt)
{
//叶子结点 或 完全连续 或 完全不连续
if(l==r||tree[rt].ml==r-l+1||!tree[rt].ml) return tree[rt].ml;
if(x<(l+r)/2+1-tree[rt<<1].rl) return query(x,l,(l+r)/2,rt<<1);//在中间地带左侧
if(x>(l+r)/2+tree[rt<<1|1].ll) return query(x,(l+r)/2+1,r,rt<<1|1);//在中间地带右侧
return tree[rt<<1].rl+tree[rt<<1|1].ll;//在中间地带直接返回加合
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
build(1,n,1);
while(m--)
{
getchar();
scanf("%c",&c);
if(c=='D') scanf("%d",&x),update(x,0,1,n,1),sk.push(x);
else if(c=='R') update(sk.top(),1,1,n,1),sk.pop();
else scanf("%d",&x),printf("%d\n",query(x,1,n,1));
}
}
return 0;
}