发现自己以前就没有真正理解透彻过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);
}