1.可持久化線段樹(可持久化陣列)
最基礎的可持久化資料結構,每次修改開新的log個點即可。
#includeusing namespace std;
const int n=1e6+100;
templatevoid rd(t &x)
templatevoid print(t x)
struct segseg[n*20];
int tot,edit[n];
int n,m,a[n],num=0;
int build(int l,int r)
int mid=(l+r)>>1;
seg[nw].ls=build(l,mid);
seg[nw].rs=build(mid+1,r);
return nw;
}int cg(int bf,int to,int l,int r,int w)
int mid=(l+r)>>1;
if(to<=mid)seg[nw].ls=cg(seg[bf].ls,to,l,mid,w);
else seg[nw].rs=cg(seg[bf].rs,to,mid+1,r,w);
return nw;
}int qry(int nw,int to,int l,int r)
int main()
else
}}
靜態區間第k小,很久以前寫的也貼在這了
#pragma gcc optimize(3,"inline","ofast")
#includeusing namespace std;
const int n=2e5;
int a[n+5],b[n+5],ls[n*20],rs[n*20],rt[n+5],seg[n*20],tot=0;
void read(int &x)
int build(int l,int r)
int add(int bf,int l,int r,int x)
int query(int a,int b,int l,int r,int k)
int main()
for(int i=1;i<=m;i++)
return 0;
}
2.可持久化平衡樹(fhq treap)
模板題
基本和之前線段樹一樣,mg和split發生改變的點都開成新點即可。個人覺得為了減少空間消耗可以分別寫開新點和不開新點的mg/split,但懶得寫......
#includeusing namespace std;
const int n=5e5+100;
const int inf=int_max;
templatevoid rd(t &x)
struct ggnd[n*60];
int n,edit[n],tot=0;
void push_up(int x)
int mg(int x,int y)
else }
void split1(int nw,int k,int &x,int &y)
}void split2(int nw,int k,int &x,int &y)
}void p(int x)
int new_node(int x)
void ins(int val,int id,int nw)
void del(int val,int id,int nw)
void rnk(int val,int id,int nw)
void kth(int k,int id,int nw)
void pre(int val,int id,int nw)
void nxt(int val,int id,int nw)
int main()
}
可持久化文藝平衡樹
翻轉也是一樣的,注意每次push_down也要開新點,不然會影響歷史版本,而這裡按我之前那種較標準的翻轉寫法(見lct篇)會比較翔而且空間常數可能還要×2,因此這裡用了最簡單的翻轉寫法(即當前點有翻轉標記但是其實並未翻轉)
ps:這種簡單的翻轉寫法可能在一些題裡要多push_down幾次保證當前點的左右孩子順序是實際的順序,原來的寫法則不用,因此個人在沒有特殊要求下更傾向於寫原來的那種
#include#define ll long long
using namespace std;
const int n=2e5+100;
const int m=3e7+100;
templatevoid rd(t &x)
struct ggnd[m];
int n,edit[n],tot=0;ll las_ans=0;
void push_up(int x)
void push_down(int x)
}int mg(int x,int y)
else }
void split1(int nw,int k,int &x,int &y)
}int new_node(int x)
void ins(int ps,int val,int id,int nw)
void del(int ps,int id,int nw)
void reverse(int l,int r,int id,int nw)
void qry(int l,int r,int id,int nw)
int main()
}
3.可持久化並查集
用可持久化線段樹維護每個點每個版本的父親,因為不能加路徑壓縮所以只用按秩合併保證樹高在log,所以每次修改查到乙個點父親要log^2的複雜度,總複雜度n*log^2(注意這題詢問數和節點數並不相同,空間要算好......)
#include#define pii pair#define fi first
#define sc second
using namespace std;
const int n=2e5+100,m=8e6+100;
templatevoid rd(t &x)
struct segseg[m];
int n,m,tot=0;
int edit[n];
int build(int l,int r)
int mid=(l+r)>>1;
seg[nw].ls=build(l,mid),seg[nw].rs=build(mid+1,r);
return nw;
}pii qry(int nw,int l,int r,int to)
pii find_fa(int id,int x)
return nw;
}int cg(int bf,int l,int r,int to,pii val)
return nw;
}int main()
if(op==2)edit[i]=edit[x];
if(op==3)
}}
4.可持久化trie樹
這個東西其實和線段樹平衡樹差不多,插入乙個數多log個點,全都新開即可。
看到洛谷訓練有個專題叫可持久化trie,本來打算單獨開一篇部落格刷那裡的題,結果發現沒一道是真的trie......所以就去再用可持久化trie寫了一波可持久化平衡樹模板題.
#includeusing namespace std;
const int n=5e5+100;
const int m=2e7+100;
const int inf=1e9+5,lim=1<<30;
templatevoid rd(t &x)
int n,nxt[m][2],sz[m],tot=0,edit[n];
void push_up(int x)
int ins(int bf,int x,int mo)
if(x&mo)nxt[nw][1]=ins(nxt[bf][1],x,mo>>1);
else nxt[nw][0]=ins(nxt[bf][0],x,mo>>1);
push_up(nw);
return nw;
}int del(int bf,int x,int mo)
if((x&mo)&&nxt[bf][1])nxt[nw][1]=del(nxt[bf][1],x,mo>>1);
if(!(x&mo)&&nxt[bf][0])nxt[nw][0]=del(nxt[bf][0],x,mo>>1);
push_up(nw);return nw;
}int rnk(int nw,int x,int mo)
int rnk_sam(int nw,int x,int mo)
int kth(int nw,int x,int mo)
int main()
}}
可持久化資料結構
用vector實現可持久化 這題要求的是乙個支援區間查詢的可持久化資料結構。這裡使用vector巧妙地實現 pair用pair儲存時間戳以及當前時間的值,query的時候使用二分查詢即可。如下 1 include2 include3 include4 include5 define x first ...
可持久化資料結構維護可持久化陣列
首先我們要知道,undo 操作,也就是直接跳回前面的操作 歷史操作 然後跳回的地方到現在的地方這乙個區間的操作都不用管。這就是高階挑戰的思路 可持久化是指一種可以訪問歷史版本的資料結構 然後我們就可以知道,詢問歷史,又是陣列,也就是可持久化陣列。可持久化陣列的維護很簡單,我們可以開乙個 o n 2 ...
可持久化 可持久化資料結構學習筆記
我終於也要學可持久化了qwq 膜wjmzbmr 線 割 分 是 我 資料結構的可持久化,就是把乙個資料結構的歷史狀態全都儲存下來,從而能夠快速查詢之前出現過的某個操作的結果。當然這必然會帶來很大的時間和空間消耗,因此優越的可持久化都會充分利用資料結構歷史狀態裡的相似部分來減少時間和空間複雜度。顯然有...