关于fhq treap的一些思考

发现自己以前就没有真正理解透彻过fhq treap和大部分平衡树

最近在被这题[NOI2005]维护数列榨干的过程中,发现了一些理解性的漏洞,在这里写出来,也供大家参考和改正。

不过我可能还没有考虑周全,如有遗漏和错误,欢迎指出.

一开始期盼地交了一发:10分...{>~<}

在对拍的时候,我被这样的一组简单的数据(经过整理,原数据太脑残orz)hack了:

9 5
-8 10 -5 -6 -7 2 8 -7 -1
REVERSE 8 1
REVERSE 3 3
MAX-SUM
REVERSE 3 6
MAX-SUM

然鹅并不知道为什么错...

弱弱地查阅了许多资料,询问了很多疑惑,又自己手画思考发现可能错在以下几点:

1.

\(Merge\)的时候,为下传标记,我在翻转区间时的习惯写法是这样:

int Merge(int x,int y){
    if(!x||!y) return x+y;
    return a[x].d<a[y].d?down(x),a[x].r=Merge(a[x].r,y),Upd(x),x
                       :(down(y),a[y].l=Merge(x,a[y].l),Upd(y),y);
    //down即下传标记
}

这在仅要求区间翻转的题目中是完全可以的emm...

但这题布星

我们考虑这样的情况:

对,就是在有一课树为时,会出问题。

这题要求我们动态维护一个最大区间子段和,我们会用三个值分别表示一段区间的从左边开始的最大子段和、从右边开始的最大子段和以及总的最大子段和,注意,在翻转的时候,我们不仅要交换左右儿子,同时也要交换当前点的左右最大子段和长度(因为序列被翻转了)

而如果要合并的两棵树中有空树,代码中就会直接返回另一棵树而不进行down,我们考虑这样造成的影响。

如果在\(Merge\)的过程中我们一路\(Merge\)到底,则两棵树的两条链会下传标记。一旦有一颗树变空,另一棵树的根节点就不会下传标记,但是下传标记在这里是次要的,注意上面有讲到,下传标记的同时我们会交换左右的最大子段和,而这两个值是会影响那个根节点的父亲的更新的。

这里给出我下传翻转标记更新最大子段和的代码

il vd Upd_r(int u){a[u].fr^=1,swap(a[u].l,a[u].r),swap(a[u].lm,a[u].rm);} 
// 翻转 fr为标记
a[u].lm=Max(a[lu].lm,a[lu].sum+a[u].v+a[ru].lm), //左边
a[u].rm=Max(a[ru].rm,a[ru].sum+a[u].v+a[lu].rm), //右边
a[u].sm=Max(Max(lu?a[lu].sm:INF,ru?a[ru].sm:INF),a[lu].rm+a[u].v+a[ru].lm); //总
// 最大子段和

可以看到,父亲的更新受到儿子的值的影响。

所以\(Merge\)函数需稍作更改:

int Merge(int x,int y){
    down(x),down(y);
    if(!x||!y) return x+y;
    return a[x].d<a[y].d?a[x].r=Merge(a[x].r,y),Upd(x),x:(a[y].l=Merge(x,a[y].l),Upd(y),y);
}

猜你喜欢

转载自www.cnblogs.com/Shallowy/p/10028281.html