浅谈左偏树的pop操作

void pop(int x){
    S[x].val = -1;
    S[ls].rt = ls, S[rs].rt = rs;
    S[x].rt = merge(ls, rs);
}
while(S[get(u)].sum > m) pop(get(u));

这是我一开始写的pop操作,和学的。乍一看似乎没什么问题。但是当我去写 $P1552 [APIO2012] 派遣 $ 的时候我才发现不对劲 WA。 我先是看了眼题解,但是他们大多都是

int pop(int x) {
    S[x].sum = S[x].val = S[x].siz = 0;
    S[ls].rt = ls, S[rs].rt = rs;
    return merge(ls, rs);
}  
while(S[S[u].rt].sum > m ) 
   S[u].rt = pop(S[u].rt);

这种不友好写法(就没有不返回值写法的吗)。
于是我开始疯狂调试之路。 在经历了整整一页的艰苦奋斗,我最终发现了问题

while(S[get(u)].sum > m) pop(get(u));

上述代码在进行 $pop$ 操作时,当弹出一个点后,其儿子与新根会有断层。 即 $pop$ 中 $ls$ 和 $rs$ 分别指向了自己,而新的根是 $ls$ 与 $rs$ 中的其中一个。假定落于 $ls$, 如果 $u$ 处于 $rs$。 则一次 $pop$ 操作之后再次进行 $get(u)$ 时,只能找到 $rs$ 而非新根 $ls$。 综上我给出我改过之后的 $pop$ 操作。

void pop(int x)
{
    S[x].sum = S[x].val = S[x].siz = 0;
    S[x].rt = S[ls].rt = S[rs].rt = merge(ls, rs);
}
posted @ 2023-09-20 14:45  Saka_Noa  阅读(13)  评论(0)    收藏  举报  来源