链接:点击打开链接
给你N个相连的村庄,有三种操作,D x是把x村庄销毁(就是这个村庄与其他村庄的路都毁掉),Q x是问你有几个村庄与之相连,R是把最近销毁的村庄复原。
求的是连续的区间,区间合并问题,村庄左连续和右连续的村庄,就可以得出总连续的村庄。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 50010
int treel[4*N];
int treer[4*N];
int mark[N];
void bulid(int l,int r,int n){
treel[n]=treer[n]=r-l+1;//初始化左连续相连的长度和右连续相连的长度
if(l==r)
return;
int mid;
mid=(l+r)>>1;
bulid(l,mid,2*n);
bulid(mid+1,r,2*n+1);
}
void update(int l,int r,int n,int a,int op){
if(l==r&&l==a){
if(op==1){//摧毁
treel[n]=0;
treer[n]=0;
}
else{//修复
treel[n]=1;
treer[n]=1;
}
return;
}
int mid;
mid=(l+r)>>1;
if(a<=mid)
update(l,mid,2*n,a,op);
else
update(mid+1,r,2*n+1,a,op);
treel[n]=treel[2*n];//更新
treer[n]=treer[2*n+1];
if(treel[2*n]==mid-l+1)
treel[n]+=treel[2*n+1];
if(treel[2*n+1]==r-mid)
treer[n]+=treer[2*n];
}
int query(int l,int r,int n,int a){
if(treel[n]==r-l+1) //连续区间
return treel[n];
if(l==r)
return 0;
int mid=(l+r)>>1;
if(a<=mid){
if(mid-treer[2*n]<a)//最右值减去最右值还小于a,说明就在在这个区间里,就不用往下搜啦
return treer[2*n]+treel[2*n+1];
return query(l,mid,2*n,a);
}
else{
if(mid+1+treel[2*n+1]>a)//同理,最左值加最左值还大于a,说明就在这个区间,就不用往下搜啦
return treer[n*2]+treel[2*n+1];
return query(mid+1,r,2*n+1,a);
}
return -1;
}
int main(){
int n,m,ok,a;
char c;
while(~scanf("%d %d",&n,&m)){
bulid(1,n,1);
ok=0;
while(m--){
getchar();
scanf("%c",&c);
if(c!='R'){
scanf("%d",&a);
if(c=='Q')
printf("%d\n",query(1,n,1,a));
else{
mark[ok]=a;
ok++;
update(1,n,1,a,1);
}
}
else{
ok--;
update(1,n,1,mark[ok],2);
}
}
}
return 0;
}