可持久化,就是按照時間(例如t個時間點),每乙個時間點建立乙個資料結構。暴力做肯定會炸,所以通過指向歷史節點的方法來節省空間,時間,從而實現訪問歷史情形。
至於具體是如何指向歷史節點的,請直接看下面的例子
(這個詳講,後面的資料結構都差不多同理,畢竟我懶)
舉個版題:poj 2104
題意就是給出乙個陣列(10w),和一些詢問(5k),每次詢問給出乙個區間[a,b](陣列下標)和k,表示求[a,b]中第k大的數。
這道題有很多種解法,這裡只討論可持久化線段樹做法
我們可以先讀入資料,離散化,按照陣列下標劃分時間版本(即輸入陣列第乙個數後版本為1,輸入第二個數後版本為2),然後將線段樹維護的值sum定義為:在這個版本上的所有數,在[l,r]區間內的有多少個。這樣的話詢問陣列下標[a,b]中第k大時,就可以將版本b和版本a-1的同一區間的維護值相減,來得到[a,b]的實際sum,然後在樹里按往常的方法搜,即,如果當前節點左兒子(兩個版本相減)的sum大於等於當前k,就往左找,否則往右找,到葉子時,這個節點的[l,r],l或者r(l=r)就是要找的數。
當我們讀入第i個數時(記為a),加入,那麼線段樹中受影響的節點數只有log(n)個,它跟上乙個版本不一樣節點的也只有log(n)個那麼僅新建受影響的節點,sum=上乙個版本sum+1,就可以了,這些節點的左右兒子,除了指向新建的節點的,都指向上乙個版本的節點。
ps:建議將未加入任何數的版本build出來作為0版本
舉個例子:
形象的說,只有從上往下的一條線上的節點改了,數量就是線段樹深度log(n)
附上爆醜無比的 ac**:
#include
#include
#define maxn 100006
using namespace std;
int b[maxn],aa,bb,a[maxn],root[maxn];
int n,m,cnt,bn,k;
struct node
i[maxn*20];
int place(long long x)
return (l+r)/2;
}void build(int now,int l,int r)
void insert(int now,int
last,int
x,int l,int r)
else
}int find(int now,int
last,int k,int l,int r)
int main()
sort(b+1,b+1+n);
bn=unique(b+1,b+1+n)-b-1;
root[0]=++cnt;
build(root[0],1,bn);
for(int i=1;i<=n;i++)
for(int t=1;t<=m;t++)
}
建立線段樹,除了葉子結點以外的其它所有節點,都只起記錄左右兒子的作用,葉子結點記錄陣列下標對應的值,同樣用之前的可持久化線段樹的方式,指向歷史節點就可以了。
並查集本體就是乙個fa陣列,只要通過可持久化線段樹實現可持久化陣列,再把並查集操作稍微改一下就可以了。
同上先說treap:由於隨機建樹期望深度是log1.39(n)的,所以直接隨機建樹,通過給每乙個數賦隨機權值,加數時除了保證平衡樹性質,同時保證子樹任意值權值大於(或小於)當前節點權值即可。合併操作時,也要如此做。
然後可持久化就可以了(滑稽)
ps:這幾個版我寫的都奇醜,就懶得粘上來了,喵。
可持久化線段樹總結(可持久化線段樹,線段樹)
最近正在學習一種資料結構 可持久化線段樹。看了網上的許多部落格,弄了幾道模板題,思路有點亂了,所以還是來總結整理下吧。你需要維護這樣的乙個長度為 n 的陣列,支援如下幾種操作 在某個歷史版本上修改某乙個位置上的值 訪問某個歷史版本上的某一位置的值 此外,每進行一次操作 對於操作2,即為生成乙個完全一...
可持久化 可持久化資料結構學習筆記
我終於也要學可持久化了qwq 膜wjmzbmr 線 割 分 是 我 資料結構的可持久化,就是把乙個資料結構的歷史狀態全都儲存下來,從而能夠快速查詢之前出現過的某個操作的結果。當然這必然會帶來很大的時間和空間消耗,因此優越的可持久化都會充分利用資料結構歷史狀態裡的相似部分來減少時間和空間複雜度。顯然有...
可持久化fhq treap學習筆記
目錄luogu扣圖 由於fhq treap是沒有旋轉操作的 所以每次操作後的其它沒有操作的節點間的關係不變 而有旋轉平衡樹是要改變的,所以就不大能進行可持久化了 回想,主席樹的方法 每次用log的記憶體記錄一次操作 這可持久平衡樹也一樣 每次merge或者split都新開節點記錄路徑 路徑是log級...