还是要有想象力……
这题需要一个栈,用来保存摧毁的顺序。可能出现一个地点,被摧毁多次,而重建时,出栈,只有第一次需要重建,后面再出栈,发现已经是建好的,就不需要再重建
例如:摧毁顺序是 5 2 4 5,重建时,5先出栈,重建。然后4、2依次出栈。5再出栈时,发现已经建好,那么就不用再重建了。
问题的关键在如何搜索与某个点直接和间接相连的点,我的思路是:首先递归找到这个点,假设是k,然后回退。如果是从r的左孩子退回来的,就去查找r的右孩子是否有断点(即,r->rt->ok==0),如果有,则肯定是离k点最近,且在k右边的断点。同理,如果从r的右孩子退回来,就查找r的左孩子是否有断点。当然,这个查找,都是第一次出现r->rt->ok==0或者r->lf->ok==0时查找。为啥必须是第一次?因为只有第一次时,去查找的断点才是离k最近的断点。这个需要想象哈。
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
typedef struct _Node{
int ok;
int size;
int st, ed;
struct _Node* lf;
struct _Node* rt;
}Node, *pNode;
int n, m;
pNode root;
void create(pNode* r, int st, int ed){
int z;
pNode w = (pNode)malloc(sizeof(Node));
*r = w;
w->st = st; w->ed = ed;
if(st==ed){
w->ok = 1;
w->size = 1;
w->lf = w->rt = 0;
return;
}
z = (st+ed)>>1;
create(&(w->lf), st, z);
create(&(w->rt), z+1, ed);
w->ok = 1;
w->size = w->lf->size + w->rt->size;
}
void update(pNode r, int v, int op){ //0, destroy; 1, rebuild;
int z = (r->st+r->ed)>>1;
if(r->st==r->ed && r->st==v){
if(!op){
r->size = 0;
r->ok = 0;
}
else{
r->size = 1;
r->ok = 1;
}
return;
}
if(v>z)
update(r->rt, v, op);
else
update(r->lf, v, op);
r->ok = r->lf->ok && r->rt->ok;
r->size = r->lf->size + r->rt->size;
}
int mkl, mkr, ll, rr;
int find(pNode r, int op){ //0, lf; 1, rt; 用来查找离k最近的断点。当查找右子树时,就找最左的点,即为比k大,且离k最近的点。同理,查找左子树,就找最右的点,即为比k小,且离k最近。
int z = (r->st+r->ed)>>1;
if(r->st==r->ed) return r->st;
if(op){
if(r->lf->ok==0)
return find(r->lf, op);
else
return find(r->rt, op);
}else{
if(r->rt->ok==0)
return find(r->rt, op);
else
return find(r->lf, op);
}
}
void search(pNode r, int v){
int z = (r->st+r->ed)>>1;
if(r->st==r->ed && r->st==v){
if(r->ok==0){
mkl = mkr = 1;
ll = rr = r->st;
rr++;
}
return;
}
if(v>z){
search(r->rt, v);
if(!mkl && !(r->lf->ok)){ //这里只触发一次哦。即,从r的右子树回来,且第一次碰上r的左子树有断点,那么从r的左子树查找下去,找到的最右边的断点,即为离k最近的断点!下面同理。
ll = find(r->lf, 0);
mkl = 1;
}
}else{
search(r->lf, v);
if(!mkr && !(r->rt->ok)){
rr = find(r->rt, 1);
mkr = 1;
}
}
}
void del(pNode r){
if(!r) return;
del(r->lf);
del(r->rt);
free(r);
}
int destroy[1000]; //栈,用来保存依次摧毁的点
int mk[50001]; //这个是用来标记某点是否被摧毁。0为未摧毁,1为已摧毁。
int p;
void main(){
int i, t;
char c;
freopen("in.txt", "r", stdin);
while(scanf("%d %d", &n, &m)!=EOF){
create(&root, 1, n);
p = 0;
memset(mk, 0, sizeof(mk));
for(i=0; i<m; i++){
getchar();
scanf("%c", &c);
if(c=='D'){
scanf("%d", &t);
if(!mk[t]){ //当重复摧毁时,就不需要再去更新了
update(root, t, 0);
mk[t] = 1;
}
destroy[p++] = t; //即便多次摧毁不更新,还是要进栈的。都怪题目说的不清楚
}else if(c=='R'){
t = destroy[--p];
if(mk[t]){ //当多次摧毁的点出栈时,就第一次需要重建,后面再出栈,发现已经重建好,就不必再update
update(root, t, 1);
mk[t] = 0;
}
}else{
scanf("%d", &t);
mkl = mkr = 0;
ll = 0; rr = n+1; //初始化为最两端!
search(root, t);
printf("%d\n", rr-ll-1); //最后search后,ll保存的是比t小,且离t最近的断点。rr保存的是...
}
}
}
}
这只是一种思路,应该有更好的解法。这程序运行,不知怎么回事,用了28M多的内存,光荣垫底。
补充:忘了加上del了……加上del函数清除内存,再次运行只用3M。