「主席樹」學習筆記
主席樹
主席樹——可持久化線段樹。話說這個名字的來歷也非常有意思,傳說是一位非常非常巨的巨佬發明的,他的名字叫做黃嘉泰。由於名字縮寫和一位竹席的縮寫一模一樣,於是就叫主席樹了……
所謂可持久化線段樹,就是可以查詢歷史更新信息的線段樹。例如我對線段樹進行了5次更新,但是需要查詢第2次更新結束後的結果……一種顯而易見的做法是每次更新重新建樹。但是這樣的空間不知爆到哪裏去了……
其實主席樹的做法也是比較天然的。考慮一次單點更新只會對線段樹的一串節點(\(logn\)個)做出修改,剩下的節點依然沒有改變。因此考慮每一次添加一批節點,使這批節點依附在原始的線段樹上。
實現
在普通的線段樹中,我們按照規則:左子樹的編號為父親2,右子樹編號為父親
同時,由於根節點維護的是總體信息。因此每次更新時,根節點一定有所改變。所以我們可以每次記錄一下,第\(i\)次更新後的線段樹的更節點編號。記為\(T[i]\)。然而只要知道根節點編號,就能夠一步一步找到其對應的線段樹。一個根節點對應一個狀態的線段樹,不同的狀態的線段樹可能有公共節點。
在代碼實現上,我們先假設其左右兒子不做更改,然後再一個一個改變下去。
int update(int pre, int L, int R, int x){ //pre表示更新前那個狀態的線段樹,[L,R]表示當前節點維護的區間,x表示當前更新的值 int cur = ++numNode; l[cur] = l[pre], r[cur] = r[pre], sum[cur] = sum[pre] + 1; if(L < R){ if(x <= (L+R)/2) l[cur] = update(l[pre], L, (L+R)/2, x); else r[cur] = update(r[pre], (L+R)/2+1, R, x); //直接改變左右兒子的編號。註意一定只改一個 } return cur;//這裏包括L==R的情況 }
註意這裏的\(update\)函數是有返回值的,返回更新成功後新的那一個節點的編號。
靜態區間第\(K\)小
采用可持久化權值線段樹。
權值線段樹查詢總體第\(K\)小並不難,和平衡樹查詢第\(K\)小非常相似。先禮散花,然後利用權值線段樹維護每個區間(葉子當然是從小到大的權值了)的數字個數。查詢時只需要從根節點往下走,每次比較其左兒子出現個數是否大於或等於\(K\)。如果左兒子中的數字個數都已經\(\geq K\)了,往左兒子走即可。否則就往右兒子走,記得此時應當繼續查詢第\(K-sum[lson]\)
註意,權值線段樹中葉子節點\([x,x]\)的值即代表數字\(x\)出現的次數
好了,現在考慮如何查詢區間。我們發現要求第\(K\)
於是問題迎刃而解
剩余部分待更…………………………
「主席樹」學習筆記