主席樹整理

2021-08-21 09:57:54 字數 1517 閱讀 7326

主席樹是一種奇怪的資料結構,好像又叫函式式線段樹。主席樹是一種可持久化資料結構,即它可以支援查詢「歷史記錄」,比如區間第k大等等。現在先說靜態的主席樹

大致思路是:先將n元素的值離散為[0, sz-1],這棵樹的每個節點記錄的是在當前字首[1...i]中,離散值處在[l, r]區間內的值的個數。

由線段樹的單點更新可以知道,在樹里更新或插入乙個元素,只需要o(lgn)的時間複雜度,即只需更新一條鏈,主席樹也是這樣,每次更新乙個新的[1...i],就和[1...i-1]比較,如果i的離散值在左兒子區間內,就只需新增左兒子,而可以與[1...i-1]公用右兒子,所以每次update只需新增lgn個節點,時間複雜度也是o(lgn)。

然後是查詢,假設有乙個查詢(ql, qr, k)表示求在ql到qr區間內第k大的數。根據字首和的思想,從root[qr]和root[ql-1]開始搜尋,在這兩個同樣表示[l, r]區間的節點中,sum[root[qr]的子孫]-sum[root[ql-1]的子孫] = 位置在[ql, qr]區間內,值在[l, r]區間內的數的個數,如果當前點的左邊區間sum值》=k,說明第k大在左兒子區間內,反之它在右兒子區間內,這樣就可以遞迴求解了。

感謝這個部落格殤雪的部落格,**非常好看而且易懂。感謝gjc大佬支援本文寫作,感謝gzx,tiandong,turing,zwz圍觀本文寫作。

上板子:

#include#include#include#include#includeusing namespace std;

const int n = 3e5+10;

int n, q, a[n];

vectorv;

int root[n], sum[n*40], lson[n*40], rson[n*40], tot, sz;

//最好開40倍空間,據實測20倍是不夠的

bool cmp(int x, int y)

int getid(int x)

void build(int &u, int l, int r)

void update(int &u, int v, int l, int r, int x)

else

}int query(int u, int v, int l, int r, int k)

int main()

sort(v.begin(), v.end());

v.erase(unique(v.begin(), v.end()), v.end());

sz = v.size(); tot = 0;

build(root[0], 0, sz-1);

for (int i = 1; i <= n; i++)

update(root[i], root[i-1], 0, sz-1, getid(a[i]));

for (int i = 1; i <= q; i++)

return 0;

}

主席樹 學習整理

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

主席樹 初學

現在才開始學主席樹 弱 不過不帶修改的話 還是很簡單的嘛。或者說應該叫可持久化線段樹?首先對數的區間進行離散化,這樣下面的a i 都預設為離散化以後的結果了。對於每個1.i開乙個線段樹,對於這個線段樹中的每乙個節點 l,r 表示1.i中在 l,r 中的數的個數。顯然這n個線段樹的形態大小是完全一樣的...

主席樹 模板

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