可持久化線段樹(主席樹)及可持久化陣列

2021-10-08 23:56:04 字數 3667 閱讀 1099

定義:可以訪問歷史版本的線段樹為可持久化線段樹

可持久化線段樹之所以可以訪問歷史版本,是因為巨集觀上看,它為每個版本維護了一棵樹。當然,如果真的對每個版本建一顆樹,時間空間複雜度都hold不住。所以建立i版本的樹時,如果某顆子樹相對於i-1版本沒有變化,就可以直接使其父親對應的指標指向i-1版本的這顆子樹。

p3834 【模板】可持久化線段樹 2(主席樹)

題目大意:給乙個長度為n的陣列,m次詢問乙個子區間的第k大。n≤2

e5,m

≤2e5

n \leq 2e5,m \leq 2e5

n≤2e5,

m≤2e

5分析:如果是一次詢問整個陣列的第k大,可以直接排序也可以通過借鑑快排達到平均o(n

)o(n)

o(n)

的複雜度。

如果有一顆權值線段樹,每個節點維護權值在[l, r]範圍內的數的個數,那麼求[1, n]的第k大,可以通過遞迴求,如果左子樹的數的個數為x, x大於等於k,遞迴查詢左子樹第k大,如果x小於等於k,遞迴查詢右子樹第k-x大。

為陣列的每個字首建立一顆權值線段樹,查詢[l, r]區間第k大時, 就可以通過樹l-1, 樹r結合起來查詢第k大,在[l, mid]的數的個數即為r樹對應個數減去l-1樹對應個數。

ac**

#include

using

namespace std;

const

int maxn =

200000+10

;struct node

}nodes[maxn *40]

;//如果權值線段樹某個節點的cnt為0,直接指向nodes[0],節約空間

//每次update新建logn個節點,注意nodes開夠

int usecnt =0;

int root[maxn]

;//各個版本的根節點

void

update

(int l,

int r,

int&rt,

int pre,

int v)

intquery

(int l,

int r,

int k,

int ltree,

int rtree)

intmain()

//用於離散化

vector<

int> vuniq = vnum;

sort

(vuniq.

begin()

, vuniq.

end())

; vuniq.

erase

(unique

(vuniq.

begin()

, vuniq.

end())

, vuniq.

end())

;for

(int i =

0; i < n;

++i)

int sz = vuniq.

size()

;for

(int i =

0; i < n;

++i)

for(

int i =

0; i < m;

++i)

//system("pause");

return0;

}

基於主席樹可以可持久化乙個陣列,令第i個葉子節點存放第i個元素的值,非葉子節點只維護指標,更新陣列的時候,新建logn個節點。注意,可持久化陣列需要初始化操作(上面的模板題可以不初始化)。

洛谷板題

ac**:

#include

using

namespace std;

const

int maxn =

1000000+10

;struct node

}nodes[maxn *30]

;int root[maxn]

;int usecnt =0;

vector<

int> vnum;

void

build

(int l,

int r,

int&rt)

int mid =

(l + r)

>>1;

build

(l, mid, nodes[rt]

.lkid)

;build

(mid +

1, r, nodes[rt]

.rkid);}

void

update

(int l,

int r,

int&rt,

int pre,

int pos,

int newv)

int mid =

(l + r)

>>1;

if(pos <= mid)

update

(l, mid, nodes[rt]

.lkid, nodes[pre]

.lkid, pos, newv)

;else

update

(mid +

1, r, nodes[rt]

.rkid, nodes[pre]

.rkid, pos, newv);}

intquery

(int l,

int r,

int rt,

int pos)

inline

intread()

intmain()

build(1

, n, root[0]

);for(

int i =

1, v, loc, val, op; i <= m;

++i)

else

}return0;

}

並查集就是乙個陣列,可以通過可持久化陣列實現可持久化並查集

先來看一看並查集的**

class

uset

~ufa()

intfindfa

(int x)

void

union

(int x,

int y)

};

上面這乙份並查集的**是否能使用可持久化陣列呢?其實是不能的,因為這份實現中,採用了路徑壓縮的優化方法,每次查詢會更改陣列多個元素,如果要實現可持久化陣列,就需要很多的額外空間,時間複雜度也會上公升。

並查集還有另外一種優化方法:按秩合併,即每次將深度小的合併到深度大的。

**如下:

class

uset

~ufa()

intfindfa

(int x)

void

union

(int x,

int y)}}

;

這樣每次合併並查集的時候只做一次單點修改,可以使用可持久化陣列實現可持久化並查集。

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

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

主席樹 可持久化線段樹

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

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

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