题意: 在一条直线上右连续的n个点,现在,给你m个操作,D x 破坏点 x。Q x 查询,点x所在的区间的最长的没被破坏的连续的点的个数。
思路:线段数的区间更新 ,节点维护区间的最长连续的点的个数,其中有两个难点,即PushUp函数,和 query(查询) 函数
具体操作即作用在注释里面都有写到。(不明白的可以评论,或者私我,有时间一定马上回复,谢谢支持!)
(query操作参考了kuangbindalao的博客,注释加上了一点我自己的理解,如有错误,欢迎指出)
#include "iostream"
#include "stack"
#include "algorithm"
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int Max=50005;
int sum[Max<<2],lsum[Max<<2],rsum[Max<<2];//分别记录总区间,从左端点开始,从右端点开始的最长连续区间
void build(int l,int r,int rt)
{
sum[rt]=lsum[rt]=rsum[rt]=r-l+1;//最初,区间的最长连续区间即本区间的长度
if(l==r) return;
int m=(l+r)>>1;
build(lson);
build(rson);
}
void PushUp(int rt,int ll,int lr)
{
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
if(lsum[rt]==ll) lsum[rt]+=lsum[rt<<1|1];//如果左区间的最长连续区间与左区间等长,说明区间没有被隔开,那么总区间的
if(rsum[rt]==lr) rsum[rt]+=rsum[rt<<1];//区间还要加上右区间的最长连续区间,对于右区间的来说也是如此
sum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1],max(sum[rt<<1],sum[rt<<1|1]));//最后,总区间的最长连续区间即为可能的三个区间的最大值
}
void update(int c,int x,int l,int r,int rt)
{
if(l==r){
sum[rt]=lsum[rt]=rsum[rt]=c?1:0;//单点更新,如果是删除操作,那么赋值为0,否则为1
return;
}
int m=(l+r)>>1;
if(x<=m) update(c,x,lson);
else update(c,x,rson);
PushUp(rt,m-l+1,r-m);//更新值
}
int query(int x,int l,int r,int rt)
{
if(l==r||sum[rt]==0||sum[rt]==r-l+1)//如果已经到了叶子节点或者最长连续区间为0或者最长连续区间为区间的长都可以直接返回
return sum[rt];
int m=(l+r)>>1;
if(x<=m){
if(x>=m-rsum[rt<<1]+1)//因为t<=mid,看左子树,m-rsum[rt<<1]+1代表左子树右边连续区间的左边界值,如果t在左子树的右区间内
return query(x,lson)+query(m+1,rson);//那么还要加上右边的一段区间
else//如果不在左子树的右边界区间内,则只需要看左子树
return query(x,lson);
}
else{
if(x<=m+1+lsum[rt<<1|1]-1)//右子树同理
return query(x,rson)+query(m,lson);
else
return query(x,rson);
}
}
int main()
{
ios::sync_with_stdio(false);
int n,m;
while(cin>>n>>m){
stack<int> s;//用一个栈来记录删除的点
if(!s.empty()) s.pop();
build(1,n,1);
char ch;
int x;
while(m--){
cin>>ch;
if(ch=='D'){
cin>>x;
s.push(x);
update(0,x,1,n,1);
}
else if(ch=='R'){
if(x>0){
x=s.top();
s.pop();
update(1,x,1,n,1);
}
}
else{
cin>>x;
cout<<query(x,1,n,1)<<endl;
}
}
}
return 0;
}