主席樹,是乙個可以以優良效能去做乙個可持久化資料的資料結構。雖時間變得較少,但空間的消耗量是在是難以承受其他的東西。
因為在題目中,為了讓選手使用主席樹,必定會將資料範圍開的極大,但實際數字個數又相對於範圍來說很小。
這時候我們不可能開乙個極大的線段樹(因為本來空間就不夠用,還這麼浪費),所以就無可避免的用到了離散化這個小小的技巧。
在c++庫函式中,有乙個函式叫做unique()。它的作用是去掉在位址為a到a+n中,相鄰的量是否重複:是,就將其放置到這段位址的最後。
簡單的來說,就是將乙個排完序後的陣列去重,返回沒有重複內容的最後位址。
用法如下:
無重複元素的最後位址【返回的值】=unique (開始位址,結束位址)
該函式stl和陣列均可使用
這麼多棵線段樹,我們也不可能建立多個結構體來儲存。我們可以把所有線段樹的節點全部放在tree結構體中,設當前有m個節點,每執行一次插入操作,新增了x個節點,則存放在tree中的第(m+1)個節點至第(m+x)個節點(當然也有別的編號方式)。同時,我們需要乙個root陣列,其中root[i]表示第i棵線段樹的根節點的編號。
這樣我們就構建完了,來想想——為什麼需要歷史版本?回到我們一開始的問題,求區間第k大,假設當前詢問為求[x,y]的第k大,則我們所需要用到的線段樹為第x+1棵到第y棵。從根節點開始,我們將第y棵樹和第x+1棵樹一一對應的節點所維護的值進行相減,其所得到的數就是在所詢問的[x,y]中,當前節點表示的子區間的那幾個數值在整個區間中出現的次數,用t表示,即t=root[y].[1,mid]-root[x-1].[1,mid]。先判斷t是否大於k,如果t大於k,那麼說明在區間[x,y]內存在[1,mid]的數的個數大於k,也就是第k大的值在[1,mid]中,否則在[mid+1,r]中。
其實必要的知識已經講得差不多了,但是我們最後還要面臨乙個問題——加入乙個數,就新建一棵線段樹。我們假設有100000個數吧,且有100000次詢問,試想這一大片龐大的線段樹森林是要占用多大的記憶體?一定會mle的(當然資料小就無所謂)。
我們有什麼辦法縮小空間需求?我們注意到,每次我們加入乙個被離散化後的數x,則從根結點開始向下更新,我們真正相對於前面一棵線段樹的差異之處是很少的!設有一顆[1,4]的線段樹,若當前插入值為3,則[1,4]的左兒子[1,2]沒有絲毫改動!如果又新建乙個,完全是浪費。這樣子,我們就有乙個方法縮小冗餘的空間了——將沒有區別的部分直接指回去!
我們要修改乙個葉子結點的值,並且不能影響舊版本的結構。
在從根結點遞迴向下尋找目標結點時,將路徑上經過的結點都複製乙份。
找到目標結點後,我們新建乙個新的葉子結點,使它的值為修改後的版本,並將它的位址返回。
對於乙個非葉子結點,它至多只有乙個子結點會被修改,那麼我們對將要被修改的子結點呼叫修改函式,那麼就得到了它修改後的兒子。
在每一步都向上返回當前結點的位址,使父結點能夠接收到修改後的子結點。
在這個過程中,只有對新建的結點的操作,沒有對舊版本的資料進行修改。
從要查詢的版本的根節點開始,像查詢普通的線段樹那樣查詢即可。
我們按照從1到n的順序依次將資料插入可持久化的線段樹中,將會得到n+1個版本的線段樹(包括初始化的版本),將其編號為0~n。
可以發現所有版本的線段樹都擁有相同的結構,它們同乙個位置上的結點的含義都相同。
考慮第i個版本的線段樹的結點p,p中儲存的值表示[1,i]這個區間中,p結點的值域中所含的元素個數;
假設我們知道了[1,r]區間中p結點的值域中所含的元素個數,也知道[1,l-1]區間中p結點的值域中所包含的元素個數,顯然用第乙個個數減去第二個個數,就可以得到[l,r]區間中的元素個數。
因此我們對於乙個查詢[l,r],同步考慮兩個根root[l-1]與root[r],用它們同乙個位置的結點的差值就表示了區間[l,r]中的元素個數,利用這個性質,從兩個根節點,向左右兒子中遞迴的查詢第k小數即可。
主席樹 初學
現在才開始學主席樹 弱 不過不帶修改的話 還是很簡單的嘛。或者說應該叫可持久化線段樹?首先對數的區間進行離散化,這樣下面的a i 都預設為離散化以後的結果了。對於每個1.i開乙個線段樹,對於這個線段樹中的每乙個節點 l,r 表示1.i中在 l,r 中的數的個數。顯然這n個線段樹的形態大小是完全一樣的...
主席樹 模板
思想 主席樹就是一顆持久化線段樹,為什麼叫持久化了,因為它可以儲存之前的線段樹版本,並且可以拿來用,從而優化空間.至於為什麼叫主席樹了,大概是因為發明這個演算法的人的名字的緣故吧 詳細說說 主席樹是一種離線資料結構,是由很多棵線段樹組成的。第i棵線段樹存的是前i個數的資訊 每乙個線段存數字的出現次數...
主席樹模板
維護n棵1 i的字首權值線段樹,每次查詢減一下就好了。poj 2104就是模板題,裸的靜態第k大,需要先離散化,不會的就用lower bound 多試試,研究研究應該就能懂。include include include include using namespace std const int m...