题意: 在一条直线上右连续的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; }