可持久化線段樹 主席樹(靜態)

2022-07-24 19:33:14 字數 2071 閱讀 8339

參考部落格

先介紹一下主席樹,主席樹也稱函式式線段樹也稱可持久化線段樹。(其實就是支援查詢歷史版本,這個在看完之後就會了解)

其實主席樹就是很多線段樹組合的總體,從它的其它稱呼也可以看出來了,其實它本質上還是線段樹。

主席樹就是利用函式式程式設計的思想來使線段樹支援詢問歷史版本、同時充分利用它們之間的共同資料來減少時間和空間消耗的增強版的線段樹。那麼它是怎麼實現的呢?

比如有4個數5 3 6 9,求區間[2,4]第2小的數。

t[i]表示第i棵線段樹的根節點編號,l[i]表示節點i的左子節點編號,r[i]表示節點i的右子節點編號,sum[i]表示節點i對應區間中數的個數。

我們先把序列離散化後是2 1 3 4。

我之前已經說了,主席樹就是很多線段樹的總體,而這些線段樹就是按給定序列的所有字首建立的。從t[0]開始建立空樹,之後依次加入第i個數建立t[i]。

注意,如果我們直接以序列的所有字首建立線段樹肯定會mle,這裡主席樹最精妙的地方就出來了。我們建立的這些線段樹的結構,維護的區間是相同的,主席樹充分利用了這些線段樹中的相同部分,大大減少了空間消耗,達到優化目的。

直接上圖,邊看圖邊理解上面的話。

圖中上面為用序列所有字首建立的線段樹,下面為所有線段樹組合成主席樹。

圖中每個節點上面為節點編號,節點下面為對應區間,節點中數為區間中含有的數的個數,後面省略了區間。

從圖中應該可以看出主席樹是怎麼充分利用這些線段樹的相同結構來減少空間消耗的。當要新建乙個線段樹時最多隻需要新增log2nlog2n個節點,相當於只更新了一條鏈,其它節點與它的前乙個線段樹公用。

建完主席樹後我們看看它是怎麼查詢區間[2,4]第2小的數的。

首先我們要了解這些線段樹是可加減的,比如我們要處理區間[l,r],那麼我們只需處理sum[t[r]]-sum[t[l-1]]就是給定序列的區間[l,r]中的數的個數。因為我們是按字首處理的,這裡看圖自己體會一下。

這裡我們要先計算res=sum[l[t[4]]]-sum[l[t[1]]]=1,即算出給定序列區間[2,4]中數的範圍在區間[1,2]的數的個數,如果它的值大於k那麼我們就應該從線段樹的根節點走到左節點找第k個數,否則我們就應該從根節點到右節點找第k-res個數,之後遞迴下去直到葉子節點,返回葉子節點對應區間即為我們查詢的數在離散化後序列中的下標。這裡返回值為3,對應離散化後序列中數3,即原序列中數6。

講到這裡,靜態的主席樹就講完了。我們算算時空複雜度。

設原序列有n個數,含有m次詢問

空間複雜度:(建空樹)4*n+(字首和更新)nlog2nlog2n

一般我們陣列大小就開nlog2nlog2n (動態不一樣,之後會講)

時間複雜度:mlog2n

給一道裸題: 【模板】可持久化線段樹 1(主席樹)

1 #include2

using

namespace

std;

3#define ll long long

4const

int maxn = 2e6+5

;5 ll tree[maxn],l[maxn<<2],r[maxn<<2],sum[maxn<<2];6

ll sz[maxn],h[maxn];

7ll n,m,al,ar,tot,k;

8int buf[17

];9 inline void read(ll &x)

14 inline void write(int

x)16 register int cnt=0;17

while(x)buf[++cnt]=(x%10)+48,x/=10;18

while(cnt)putchar(buf[cnt--]);

19 putchar('\n'

);20

}21 inline void build(ll &root,ll l,ll r)

29 inline void update(ll &root,ll l,ll r,ll pre,ll x)

39inline ll query(ll s,ll e,ll l,ll r,ll k)

46int

main()

63return0;

64 }

主席樹(可持久化線段樹)

我真弱。連主席樹都不會。主席樹相當於多個線段樹,由於相鄰兩棵線段樹的節點的值只有少許不同,因此可以對於和前一棵樹一樣的子樹乙個指標指過去,無需操作,這樣每棵樹o logn 總複雜度o nlogn 以下是區間k大 include include include define n 100005 defi...

主席樹 可持久化線段樹

首先要學會普通的線段樹,然後理解權值線段樹,而主席樹就是多個權值線段樹 我自己的理解 但是這多個權值線段樹之間有公共部分,節約了空間。它一開始是乙個空樹,後來逐個添數,記錄新增的這個數在那個範圍內,並 1,顯然它每次只更新了一條鏈,其他不需要變,這樣就有了多個版本的線段樹。如果求 l,r 範圍內第k...

可持久化線段樹(主席樹)

qwq我大概又是機房最後乙個學主席樹的了吧 其實之前一直都在講 只是沒做題 做了幾道以後發現都是乙個套路qwq關鍵就是能不能看出來要用主席樹 主要可以解決 靜態 動態區間第k大 樹上也可以 一些有關區間的帶某些限制的詢問 如出現次數等 先把模板粘上來 include include include ...