题意分析
操作:
1. 破坏某个点
2. 恢复某个点
3. 查询该点与相连的未被破坏的点的最长长度
使用线段树维护最长连续区间和,具体方法如下。
首先我们先增加两个域,llen,rlen
llen表示一个区间从最左端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[0,0,0,1,1],llen = 3,从最左端有3格可以利用
区间[1,5],覆盖情况为[1,0,0,0,0],llen = 0,因为从最左端开始找不到1格可用的区间
rlen表示一个区间从最右端开始可用的且连续的最大长度
例如区间[1,5],覆盖情况为[1,0,1,0,0],rlen = 2,从最右端有2格可以利用
区间[1,5],覆盖情况为[0,0,0,0,1],rlen = 0,因为从最右端开始找不到1格可用的区间
对于一个区间,我们知道它左半区间的tlen,和右半区间的tlen,如果左半区间的tlen >= W ,那么我们一定能在左边找到(满足最左),所以可以深入到左半区间去确定该区间的具体位置
如果左端的不满足,那么我们要先考虑横跨两边的区间(因为要满足最左),因而记录的llen,rlen可以派上用场,一段横跨的区间,
那么是 左边区间rrlen + 右边区间llen ,如果满足的话,就是该区间了,它的位置也是可以确定的
如果横跨的区间不满足,那么就在右半区间找,如果右半区间的tlen >= W , 那么可以在右半区间找到,所以深入到右半区间去确定它的具体位置,否则的话,整个查询就失败了
可见查询是建立在tlen,llen,rlen这个信息之上的,而每次查询后其实伴随着修改,而且还有专门的修改操作,这些修改操作都会改变tlen,llen,rlen的值,所以在更新的时候是时刻维护这些信息
关于这3个信息的维护
当前区间的tlen = max{ 左半区间tlen , 右半区间tlen , 左半区间rlen+右半区间llen} (这个不难理解吧,取左右较大的那个,或者横跨中间的那个)
如果左半区间全部可以用: 当前区间llen = 左半区间llen(tlen) + 右半区间llen
左半区间部分能用: 当前区间llen = 左半区间llen
如果右半区间全部能用: 当前区间rlen = 右半区间rlen(tlen) + 左半区间rlen
右半区间部分能用: 当前区间rlen = 右半区间rlen
这样就全部维护好了
如何查询呢?
首先可以确定的是单点查询,但是我们还要查询出来与其相连长度为多少,如果要查询的这个点mid左边,肯定是要向左边查询的。
但是要考虑到,如果左边连续的部分比要查询 <= 要查询的pos,这说明pos在左边的连续区间内,就要查询他右边有多少连续区间,最后求和。
同理,如果要查询的部分在mid右边,那么肯定是要向右边查询的。
如果 右边连续的部分 >= pos ,这说明pos在右边连续的区间内,就要同时查询左边有多少连续区间,最后求和。
如果不明白就再看一下这张图片
图1 红色表示连续的区间,蓝色表示要查询的位置,此时要查询的pos明显不在连续的区间内,这时无论红色区间右边有多少连续区间,都是没有意义的,因为蓝色不和图中红色相交,这时候只需要查询蓝色位置连续长度即可。
图2 红色表示连续的区间,蓝色表示要查询的位置,此时要查询的pos明显在区间内,这是红色区间右边长度对最后答案是有影响的,就需要查询右侧的长度是多少,最后求和。
只不过具体实现的时候,要考虑左右而已,主题思想就是这样。
这样就完成了维护与查询
代码总览
#include<cstdio>
#include<stack>
#include<algorithm>
using namespace std;
const int nmax = 55005;
const int INF = 0x3f3f3f3f;
typedef long long ll;
typedef struct{
int l,r;
int llen, rlen, tlen;
int mid() {return (l+r) >> 1;}
} Tree;
Tree tree[nmax<<2];
int n,m;
void build(int l, int r,int rt){
tree[rt].l = l, tree[rt].r = r;
tree[rt].rlen = tree[rt].llen = tree[rt].tlen = r - l + 1;
if(tree[rt].l == tree[rt].r) return;
build(l,tree[rt].mid(), rt<<1);
build(tree[rt].mid() + 1,r, rt<<1|1);
}
inline bool cmp(int rt){
return (tree[rt].tlen == tree[rt].r - tree[rt].l + 1);
}
void pushup(int rt){
tree[rt].tlen = max(tree[rt<<1].rlen + tree[rt<<1|1].llen, max(tree[rt<<1].tlen, tree[rt<<1|1].tlen));
tree[rt].llen = (cmp(rt<<1)) ? (tree[rt<<1].tlen + tree[rt<<1|1].llen) : tree[rt<<1].llen ;
tree[rt].rlen = (cmp(rt<<1|1)) ? (tree[rt<<1|1].tlen + tree[rt<<1].rlen) : tree[rt<<1|1].rlen;
}
void change(int type, int pos, int rt){
if(tree[rt].l == pos && tree[rt].r == pos){
tree[rt].rlen = tree[rt].llen = tree[rt].tlen = type;
return;
}
if(pos <= tree[rt].mid()) change(type,pos,rt<<1);
if(pos > tree[rt].mid()) change(type,pos,rt<<1|1);
pushup(rt);
}
int query(int pos ,int rt){
if(tree[rt].l == tree[rt].r || tree[rt].tlen == tree[rt].r - tree[rt].l + 1 || !tree[rt].tlen ) return tree[rt].tlen;
if(pos <= tree[rt].mid()) {
if(pos >= tree[rt<<1].r - tree[rt<<1].rlen + 1) return query(pos,rt<<1) + query(tree[rt].mid() + 1, rt<<1|1);
else return query(pos,rt<<1);
}else{
if(pos <= tree[rt<<1|1].llen + tree[rt<<1|1].l - 1) return query(pos,rt<<1|1) + query(tree[rt].mid(), rt<<1);
else return query(pos,rt<<1|1);
}
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
build(1,n,1);
char op[2]; int pos;
stack<int> s; while(!s.empty()) s.pop();
for(int i = 0;i<m;++i){
scanf(" %s",op);
if(op[0] == 'D'){
scanf("%d",&pos);
s.push(pos);
change(0,pos,1);
}else if(op[0] == 'Q'){
scanf("%d",&pos);
printf("%d\n",query(pos,1));
}else{
if(s.size()){
pos = s.top(); s.pop();
change(1,pos,1);
}
}
}
}
return 0;
}