P3834 模板 可持久化線段樹 2(主席樹)

2021-10-21 19:20:21 字數 3203 閱讀 9812

題目鏈結

靜態查詢區間第k小問題。

我們先要知道,權值線段樹可以維護整體區間的桶,查詢整體區間第k小。

雖然這題是查詢子區間[l,r]的第k小,但我們可以轉化成整體區間第k小問題:

我們對每個字首都建一棵權值線段樹,用於維護出現個數。

則[1,r]區間對應的線段樹減去[1,l-1]對應的線段樹後,得到的那棵線段樹就是以[l,r]為整體區間的權值線段樹。

那麼[l,r]區間內的第k小值就等於 :[1,r]區間對應的線段樹減去[1,l-1]對應的線段樹後,得到的那棵線段樹的整體區間第k小。(這裡是線段樹合併的思想,可以證明這兩顆線段樹是具有可加性的)

(兩棵線段樹相減指對應的節點的值相減)

但是如果老老實實對每個字首都建樹的話一共有2e5棵樹,肯定不行。

而且我們注意到第i棵線段樹和第i-1棵線段樹相比其實就是單點修改,所以這時候主席樹就派上用場了。

我們先建一棵空樹,版本為0,

然後依次插入n個點,第i次插入得到的線段樹版本為i 。

建樹方法有兩種,一種是把資料離散化後建樹,

一種是動態開點建可持久化權值線段樹。(名字瞎編的,大概就是這兩種線段樹的交集?)

總結一下,這題本質上就是利用了字首建樹的線段樹可加性建主席樹,把每次詢問的[l,r]區間轉化成了一棵權值線段樹的根節點區間,於是問題就成了在相減得到的那顆權值線段樹裡面,求整體區間第k小問題。

實現過程就看**吧。

//離散化建樹

#include

using

namespace std;

#define ll long long

const

int maxn =

2e5+7;

const

int mod =

1e9+7;

const ll inf =

34359738370

;int n,m;

int a[maxn]

,h[maxn]

,size;

//h為離散化後的陣列 size為離散化去重後的個數 也即是線段樹建樹區間長度[1,size]

int tree[maxn<<5]

,lc[maxn<<5]

,rc[maxn<<5]

,cnt=0;

//對離散化後的陣列建樹後, [l,r]表示離散化陣列h[l....r]中每個數在原先陣列出現次數之和

int root[maxn]

;//每個版本的根序號 第i棵樹就是對第i個字首建的樹 根序號為root[i]

//給定序列 q次查詢 [l,r]區間的第k小

//對每個字首序列建線段樹 [l,r]區間第k小就相當於[1,r]對應的線段樹減去[1,l-1]對應的線段樹之後得到的線段樹中的第k小

//線段樹1-線段樹2 指 對應每個節點的值相減

void

build

(int

&rt,

int l,

int r)

int mid=

(l+r)

>>1;

build

(lc[rt]

,l,mid)

;build

(rc[rt]

,mid+

1,r);}

void

updata

(int

&rt,

int last,

int l,

int r,

int pos)

//last線段樹版本上的單點修改

intkth

(int ltree,

int rtree,

int l,

int r,

int k)

//ltree對應區間是[1,l-1] rtree對應區間是[1,r]

//查詢的本質就是在兩棵線段樹相減得到的線段樹上查詢整體區間第k小

intmain()

//離散化步驟 先排序後去重

sort

(h+1

,h+n+1)

; size=

unique

(h+1

,h+n+1)

-h-1

;//返回的是去重後末尾的下一位 所以要-1

build

(root[0]

,1,size)

;//第0版本的線段樹是空樹

for(

int i=

1;i<=n;i++

)while

(m--

)return0;

}

//動態開點可持久化權值線段樹

#include

using

namespace std;

#define ll long long

const

int maxn =

2e5+7;

const ll inf =

34359738370

;const

int inf =

1e9+5;

int a[maxn]

;int tree[maxn*

105]

,lc[maxn*

105]

,rc[maxn*

105]

,cnt=

0,root[maxn]

;int n,m;

inline

void

updata

(int

&rt,

int last,

int l,

int r,

int pos)

intquery

(int rt1,

int rt2,

int l,

int r,

int k)

intmain()

while

(m--

)return0;

}

P3834 模板 可持久化線段樹 2(主席樹)

luogu p3834 維護乙個陣列 1 n 可以在任意區間查詢第 k 小值 m 次 1 leq n leq 2 10 5 1 leq n leq 2 10 5 10 9 leq a i leq 10 9 統計區間內各數出現次數,從小到大統計,直到小於等於當前數的數字個數剛好大於等於 k 為了提高效...

P3834 模板 可持久化線段樹 1(主席樹)

主席樹 菜雞看了乙個晚上的時間才懂。感覺網上的部落格大都大亂。我也是找到了幾遍好一點的看了一下。參考部落格 參考部落格 首先,會主席樹的前提是會線段樹。還有,我們一般在用線段樹的時候,下標位置大都是採用了i 2 和i 2 1來作為i號的左右孩子。但是,在主席樹中,我們不可以把這種思想帶過來。因為主席...

P3834 模板 可持久化線段樹 1(主席樹)

這是個非常經典的主席樹入門題 靜態區間第k小 資料已經過加強,請使用主席樹。同時請注意常數優化 如題,給定n個整數構成的序列,將對於指定的閉區間查詢其區間內的第k小值。第一行包含兩個正整數n m,分別表示序列的長度和查詢的個數。第二行包含n個整數,表示這個序列各項的數字。接下來m行每行包含三個整數l...