0%

P3369 【模板】普通平衡树——替罪羊树

思路讲解

【算法讲解150【扩展】有序表专题3-替罪羊树】 https://www.bilibili.com/video/BV1GqDSYbEPR/?share_source=copy_web&vd_source=6ca0bc05e7d6f39b07c1afd464edae37

总体上来说替罪羊树的思想还是比较简单的,但是其实真写起来还是会有挺多要注意的地方的。

prex,sufx如何改用kth和getrank实现?说白了,其实我们能保证 kth(getrank(val))valkth(getrank(val))≥val,那我们自然也可以保证 kth(getrank(val)1)<valkth(getrank(val)-1)<val

1
2
3
4
5
6
7
inline KEY prex(KEY val){ 	// 前驱定义为小于 x,且最大的数
return kth(getrank(val)-1);
}
inline KEY sufx(KEY val){ // 后继定义为大于 x,且最小的数
return kth(getrank(val+1)); // 所有<=25的数就是<26的数的个数
// kth(getrank(val)+1); -> 89851 而非 84185,是因为kth(getrank(val))本身就可能>val
}

AC代码

https://www.luogu.com.cn/record/218793914

心路历程(WA,TLE,MLE……)

解决RE,MLE是靠把删除造成重构的代码给删了(觉得没必要)

WA

https://www.luogu.com.cn/record/218789914

初步查明,是前驱后继未判断节点是否完全删除。

1
2
3
4
5
6
7
8
9
void prex(int node,KEY val){
if(key[node]>=val){
if(ls[node]!=0) prex(ls[node],val);
}else{
if(!isdel[node]) ans=max(ans,key[node]);
if(rs[node]!=0) prex(rs[node],val);
return;
}
}

当然,上面这个代码也有点问题,删除后的节点,由于没更新的缘故,需要考虑更多情况

1
2
3
4
5
6
7
8
9
10
11
12
13
void prex(int node,KEY val){
if(key[node]>=val){
if(ls[node]!=0) prex(ls[node],val);
}else{
if(!isdel[node]){ // 如果该节点没有删除,那看一看更大的,因为更小的不可能比这个优越
ans=max(ans,key[node]);
if(rs[node]!=0) prex(rs[node],val);
}else{
if(ls[node]!=0) prex(ls[node],val);
if(rs[node]!=0) prex(rs[node],val);
}
}
}

WA2

https://www.luogu.com.cn/record/218793645

初步查明,原因是删除节点后,又加入了该节点,需要修改节点状态重新为isdel=false