主席樹 初學

2021-07-09 21:48:54 字數 944 閱讀 2848

現在才開始學主席樹(←弱(。・・)ノ)。不過不帶修改的話**還是很簡單的嘛。。。或者說應該叫可持久化線段樹?

首先對數的區間進行離散化,這樣下面的a[i]都預設為離散化以後的結果了。

對於每個1..i開乙個線段樹,對於這個線段樹中的每乙個節點[l,r]表示1..i中在[l,r]中的數的個數。顯然這n個線段樹的形態大小是完全一樣的。(mle?後面解釋)。

一開始並不需要建樹,只要把a[1]..a[n]乙個個插入就行了。那麼如何插入a[i]呢?首先給線段樹i乙個根節點rt[i],區間範圍是1..m(m表示a[1]..a[n]中的最大值)。顯然相對於線段樹i-1的根節點rt[i-1],rt[i]多了乙個數,因此sum[rt[i]]=sum[rt[i-1]]+1。令mid=(1+m)/2,然後看a[i]在[1..mid]中還是[mid+1,m]中。假設在[1..mid]中,那麼顯然線段樹i的右子節點[mid+1,m]的內容和線段樹i-1的右子節點[mid+1,m]是一模一樣的,只需要乙個指標過去就行了而不需要再建;然後遞迴建立(ls[rt])。a[i]在[mid+1,m]中也是同理的。這樣每層只有乙個子節點需要遞迴,因此對於i只需要新的logn的空間,同時插入也是logn的。

然後是查詢(x,y,k)。首先限定範圍l=1,r=m,同時找到u=rt[x-1],v=rt[y]。注意到,在x-y中[1,m]內的數的個數就是sum[v]-sum[u]!換句話說對於任意u,v,它們對應的區間都是[l,r]的話,且u所在的線段樹是x,v所在的線段樹是y,那麼x+1..y中在[l,r]中的數的個數就是sum[v]-sum[u]!因此,我們可以判斷[1,m]中數的個數是否》=k,是就查詢[1,(m+1)/2],否則查詢[(m+1)/2,m]。然後不斷遞迴操作即可。顯然,時間複雜度是o(logn)。

對於修改,我們可以發現每乙個線段樹相當於儲存乙個字首和!實際上修改+查詢相當於單點修改字首查詢。那麼我們可以用樹狀陣列維護。具體不闡述了。

by lych

2016.2.11

主席樹 模板

思想 主席樹就是一顆持久化線段樹,為什麼叫持久化了,因為它可以儲存之前的線段樹版本,並且可以拿來用,從而優化空間.至於為什麼叫主席樹了,大概是因為發明這個演算法的人的名字的緣故吧 詳細說說 主席樹是一種離線資料結構,是由很多棵線段樹組成的。第i棵線段樹存的是前i個數的資訊 每乙個線段存數字的出現次數...

主席樹模板

維護n棵1 i的字首權值線段樹,每次查詢減一下就好了。poj 2104就是模板題,裸的靜態第k大,需要先離散化,不會的就用lower bound 多試試,研究研究應該就能懂。include include include include using namespace std const int m...

模板 主席樹

譴責奶人的hsz巨神 靜態版本 求區間第k大 可持久化也沒那麼高大上嘛,主席樹本質上就是多棵線段樹,求第k大就類似平衡樹的第k大。stay foolish,stay hungry,stay young,stay include include include include using namesp...