還是要有想象力……
這題需要一個棧,用來保存摧毀的順序。可能出現一個地點,被摧毀多次,而重建時,出棧,只有第一次需要重建,後面再出棧,發現已經是建好的,就不需要再重建
例如:摧毀順序是 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。