1. 程式人生 > >關於fhq treap的一些思考

關於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);
}