可持久化線段樹學習筆記

2022-04-12 01:46:33 字數 1542 閱讀 8097

可持久化,即對資料修改後仍可查詢到其歷史版本。

以模板題為例:

p3919 【模板】可持久化線段樹 1(可持久化陣列)

單點修改、查詢的可持久化。

暴力時空複雜度:o(nm(版本複製)+m(修改查詢)),可持久化線段樹 時空複雜度只為:o(m log n+n)

題解口胡:

建乙個陣列hed存各版本的對應的線段樹根

對於修改操作:對修改了的部分進行新建,沒有修改的部分共用。因修改而新建的部分不只有葉子節點,還應有其到根節點的路徑,否則無法維護線段樹的結構。

對於查詢操作:使hed[當前版本數]=hed[查詢版本數],查詢就完事了。(當時還建個新根,連原版本的左右兒子。這都是多此一舉)

陣列大小(本題單點修改):4*n(一棵線段樹(因為用了動態開點,實際一棵線段樹的空間占用要更少))+m (ceil(log n) +1)(長度為n的區間的線段樹最大深度為ceil(log n)+1

踩過的坑:

呼叫修改或查詢函式裡表區間的l,r形參要為1,n,結果直接寫成l和r了,沒注意意義。

查詢函式裡面的遞迴原樹上的點要跟著走

模板題ac**:

#include#include

using

namespace

std;

const

int n=1e6+6

;int n,m,hed[n],num[n*24],ls[n*24],rs[n*24

],cnt;

inline

intread()

void build(int t,int l,int

r) ls[t]=++cnt;

build(cnt,l,(l+r)>>1

); rs[t]=++cnt;

build(cnt,((l+r)>>1)+1

,r);

}void modify(int u,int t,int l,int r,int w,int

v)

if(w<=(l+r)>>1

)

else

}int fin(int t,int l,int r,int

w)int

main()

else

}return0;

}

ac**

理解時可以以需求導向(暴力演算法又慢又佔空間)理解

核心:共用記憶體,盡可能少開點(只新建修改的節點)。核心也會適用於一些可持久化線段樹以後的變式。

乙個操作處理完成後,以後這個操作對應的那棵線段樹都不會再變了。(方便深入理解,但不可固化思維)

注意點:序列長度為n,運算元為m,單點修改情況下可持久化線段樹空間應開:4*n+m (ceil(log n) +1)

應用:1、對陣列可持久化簡單維護:

單點修改,簡單區間修改(配合標記永久化,單次操作 新建節點數小於 4* (ceil(log n) +1)、時間複雜度o(logn))

2、配合主席樹

可持久化線段樹學習筆記

模板 includeusing namespace std const int n 2e5 10 int cnt,rt n int n,a n t n t int ls n 20 rs n 20 dat n 20 void copy int x,int y int build int l,int r...

學習筆記 可持久化線段樹

例題 luogu 3919 特點 在普通線段樹支援查詢當前狀態基礎上,支援查詢過去的所有版本 一 text 暴力儲存過去版本,每一次修改都會造成 nlogn 的時空複雜度,總複雜度 o mnlogn 直接上天 注意到每一次單點修改只會導致對應的葉子節點以及它的所有祖先儲存的值改變,我們可以考慮只儲存...

可持久化線段樹學習筆記

可持久化是真的毒瘤,在網上找了很多資料才搞懂 不過我覺得應該是我太蒻了 首先以洛谷上的兩個板子題為例吧 對於第一題,要求詢問區間第k大 第k大指的是從小到大排序的第k個 直接掃是肯定不行的,因此我們需要可持久化線段樹 感覺跳的好快,但是我也不清楚要怎麼表達,就這樣吧,反正知道是要用這個就行了 首先既...