定義:可以訪問歷史版本的線段樹為可持久化線段樹
可持久化線段樹之所以可以訪問歷史版本,是因為巨集觀上看,它為每個版本維護了一棵樹。當然,如果真的對每個版本建一顆樹,時間空間複雜度都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 ...