知識學習 主席樹

2022-05-02 01:54:23 字數 1780 閱讀 1180

這兩天學習了主席樹,基本上搞懂了主席樹是怎麼操作的

主席樹,是一種可持久化線段樹。最簡單的操作就是維護靜態區間第 \(k\) 小

主席樹通過維護歷史版本,實現查詢區間的有關操作

假設現在有這麼乙個序列:\(4, 1, 3, 5, 2\)

問如何求出區間 \([1,3]\) 內大小為第二的數?

利用大眼觀察法,很顯然是3

那麼讓計算機去怎麼實現呢?它又沒有眼睛

對於這個序列,我們可以先建一顆空的權值線段樹,命名為「樹 \(0\) 」(方便後面的使用),如圖:

現在序列裡面第乙個數是 \(4\),我們往樹裡面插入乙個 \(4\) ,因為要保留歷史版本,所以我們對 \(4\) 這個數新建一顆線段樹,命名為「樹 \(1\) 」,如圖:

為什麼是這樣呢?

\(1\le 4 \le5\),故區間 \([1,5]++\);

\(4\le 4 \le5\),故區間 \([4,5]++\);

\(4\le 4 \le4\),故區間 \([4,4]++\);

其他的還是 \(0\) ;

懂了沒有。。。

繼續插入第二個數 \(1\) ,建成「樹 \(2\) 」 ,這裡不解釋了

再插入第三個數 \(3\),建成「樹 \(3\) 」

ok!現在我們就已經可以求出 \([1,3]\) 內的大小為第二大的數了

遞迴操作查詢排名應該都會吧?

不會的看這裡:

現在你明白了主席樹是怎麼操作了的吧?

但是有乙個問題:上面我們求的是區間\([1,r]\)的第 \(k\) 大的數

同理,區間 \([1,r]\) (\(r\in\)

\([1,n]\) , \(r \in n\))的第 \(k\) 大數我們也就會求了

那怎麼求區間 \([l,r]\) 的第 \(k\) 大數呢?

舉個例子,求區間\([2,3]\)的第 \(k\) 大數

我們拿建出來的「樹 \(3\) 」減掉「樹 \(1\) 」後,再進行如上操作就可以了

這也就是字首和思想

所以對於區間\([l,r]\) 我們拿「樹 \(r\) 」減去「樹 \((l-1)\) 」,再query一下就可以求得答案了

還有乙個問題:我每個數都開乙個線段樹來存,空間不會炸掉嗎?

所以主席樹是這樣操作的:

例題:【模板】可持久化線段樹1(主席樹)

#include #define n (200000+5)

#define ls ch[rt][0]

#define rs ch[rt][1]

#define vl ch[vs][0]

#define vr ch[vs][1]

using namespace std;

int n,m,q;

int rt[n],ch[n<<5][2],tot,val[n<<5];//rt:每個線段樹的根,ch:左右兒子,tot:編號總數,val:權值

int a[n],b[n];//a:原陣列,b:離散化陣列

inline int query(int x)

inline void update(int &rt,int vs,int l,int r,int k)

inline int query(int rt,int vs,int l,int r,int k)

int main()

while(q--)

return 0;

}

主席樹學習記錄

搞了一段時間網路流後,最後還是回到了資料結構。主席樹,引fotile主席的一段話 這個東西是當初我弱不會劃分樹的時候寫出來替代的乙個玩意.被 有用心的人取了很奇怪的名字 想法是對原序列的每乙個字首 1.i 建立出一顆線段樹維護值域上每個數的出現次數,然後發現這樣的樹是可以減的,然後就沒有然後了 其實...

主席樹學習筆記

問題 給定乙個n個數的序列,q次詢問第x個數到第y個數中的第k最值。我們假定是第k小。為了使討論更加簡便,我們假定序列的每個數都是不大於n的正整數。當然一般題目中元素範圍很大,但是可以用離散化預處理來做到這一點。考慮乙個比較高階的做法 令g i j 為前i個數中,值為j的數的個數。很容易用o n 2...

主席樹 學習整理

題意 給出1e5個數,向你提5000問,l到r間的第k個數是多少?主席樹,又叫可持久化線段樹或者函式式線段樹。我只知道這些了,據說是一位大神沒學會劃分樹就自己搞了這麼個替代品,結果,比原來的還要強。說一下思路 因為是求第k大的數,所以線段樹是不行的,因為數是排好序的,所以伸展樹好像也不好解決。所以主...