做kuangbin线段树专题的时候遇到的题目,感觉这是一道非常有趣的题目。线段树的题目做的比较少,没有见过这种通过线段树在区间上做文章的题目,感觉做完这个题目之后加深了对线段树左右孩子所覆盖的区间之间的关系的认识。
题意:1-n个地道,m个次操作,D代表摧毁第i个地道,Q代表查询包含第i个地道的最大连续地道数目,并输出。R代表修复最近摧毁的那个地道;
解题的关键思路&过程:这个题目一开始我完全想不到利用左右孩子所覆盖的区间之间的关系来做到查询最大连续区间,感觉这个题目和线段树基本操作的对线段树的所利用的性质不同,这里充分利用了相邻的节点之间区间都是连续的这个性质,这个虽然是个基本的性质,但是平时着重与对左右孩子之间关系利用,有点忽视非相同父节点的同一层的叶子节点之间的关系(菜~_~)。这个题目利用tree[i].ll和tree[i].rr这两个元素分别记录这个节点所包含的区间从最左边起的最大连续区间,和从最右边起的最大连续区间。
想一下为什么要这么做呢??
感觉这正是巧妙的地方,正是这样就可以求得同一层叶子节点的最大连续区间,同层的最大连续区间只可能是你包含你查询的那个地道i的区间的节点,加上左边那个节点的的最左边起的最大连续区间或着加上右边那个节点的从最右边起的最大连续区间;
再想一下为什么最大连续区间只能是最多相邻两个节点的加和呢?因为如果当前层有三个节点的区间都连续,那查找时一定会在当前层的上一层节点就结束,不会再查到此层。
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
struct node{
int l,r;
int ll,rr,len;
}tree[4*50050];
void build(int i,int l,int r){
tree[i].l=l;tree[i].r=r;
tree[i].ll=tree[i].rr=r-l+1;
tree[i].len=r-l+1;
if(l==r){
return;
}
build(i<<1,l,(int)floor((r+l)/2.0));
build((i<<1)+1,(int)floor((r+l)/2.0)+1,r);
}
void update(int i,int x,int cnt){
if(tree[i].l==tree[i].r){
tree[i].ll=tree[i].rr=cnt;
return;
}
int mid=(tree[i].l+tree[i].r)>>1;
if(mid>=x){
update(i<<1,x,cnt);
}
else{
update((i<<1)+1,x,cnt);
}
if(tree[(i<<1)].ll+tree[(i<<1)+1].rr==tree[i].len)
tree[i].ll=tree[i].rr=tree[i].len;
else{
if(tree[i<<1].ll==tree[i<<1].len)
tree[i].ll=tree[i<<1].len+tree[(i<<1)+1].ll;
else
tree[i].ll=tree[i<<1].ll;
if(tree[(i<<1)+1].rr==tree[(i<<1)+1].len)
tree[i].rr=tree[(i<<1)+1].len+tree[i<<1].rr;
else
tree[i].rr=tree[(i<<1)+1].rr;
}
}
int que(int i,int x){
if(i==1){
if(tree[i].ll){
if((tree[i].l+tree[i].ll-1)>=x)
return tree[i].ll;
}
if(tree[i].rr){
if((tree[i].r-tree[i].rr+1)<=x)
return tree[i].rr;
}
}
if(tree[i].ll){
if((tree[i].l+tree[i].ll-1)>=x)
return tree[i].ll+tree[i-1].rr;
}
if(tree[i].rr){
if((tree[i].r-tree[i].rr+1)<=x)
return tree[i].rr+tree[i+1].ll;
}
if(tree[i].l==tree[i].r)
return 0;
int mid=(tree[i].l+tree[i].r)>>1;
if(mid>=x)
return que(i<<1,x);
else
return que((i<<1)+1,x);
}
int main(){
int n,m;
int q[50005];
while(cin>>n>>m){
build(1,1,n);
int flag=0;
for(int i=1;i<=m;i++){
char c[5];
cin>>c;
if(c[0]=='D'){
int k;
cin>>k;
q[++flag]=k;
update(1,k,0);
}
else if(c[0]=='Q'){
int k;
cin>>k;
cout<<que(1,k)<<endl;
}
else{
if(flag==0)continue;
int k=q[flag--];
update(1,k,1);
}
}
}
return 0;
}