可持续化线段树
可持續化線段樹,也叫主席樹,很久以前【也沒多久】寫了一篇求區間第k大的,那時候理解不深,現在再寫一個加深理解
可持續化,具體來說就是可以訪問歷史版本,以實現跨時間的操作。
怎么實現呢? 試想我們修改線段樹的時候,修改前是舊版本,修改后是新版本,想想新版本和舊版本有什么區別? 區別就在于從根節點到被修改點的路徑上的點改變了,其他的點是不變的
那么這個時候我們新建一個根節點,對于不變的兒子指向舊版本的兒子,對于有變化的兒子就新建一個節點 這樣一來我們有每一個時刻的根節點,就可以訪問到每個時刻的線段樹 空間會不會暴呢? 每條路徑只有logn個點,m次修改我們只多了mlogn個點,可以承受 但實在空間卡得很緊時就要用指針了
比如洛谷P3931
下面給出指針版題解代碼【并非完全是普遍的寫法,由題簡化】: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long int #define REP(i,n) for (int i = 1; i <= (n); i++) #define fo(i,x,y) for (int i = (x); i <= (y); i++) #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next) using namespace std; const int maxn = 1000005,maxm = 10000005,INF = 1000000000;inline int read(){int out = 0,flag = 1;char c = getchar();while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}return out * flag; }int N,M,A[maxn];struct node{int v;node *ls,*rs;node() {ls = rs = NULL;} }; node* rt[maxn]; int rti = 0,pos;void Build(node **u,int l,int r){*u = new node;if (l == r){(*u)->v = A[l];}else {int mid = l + r >> 1;Build(&(*u)->ls,l,mid);Build(&(*u)->rs,mid + 1,r);} }void change(node *pre,node **u,int l,int r,int v){(*u) = new node;*(*u) = *pre;if (l == r){(*u)->v = v;}else {int mid = l + r >> 1;if (mid >= pos) change(pre->ls,&(*u)->ls,l,mid,v);else change(pre->rs,&(*u)->rs,mid + 1,r,v);} }int Query(node *u,int l,int r){if (l == r) return u->v;else {int mid = l + r >> 1;if (mid >= pos) return Query(u->ls,l,mid);else return Query(u->rs,mid + 1,r);} }int main() {N = read();M = read();REP(i,N) A[i] = read();Build(&rt[0],1,N);int pre,cmd,v;while (M--){pre = read();cmd = read();pos = read();if (cmd & 1){v = read();change(rt[pre],&rt[++rti],1,N,v);}else {printf("%d\n",Query(rt[++rti] = rt[pre],1,N));}}return 0; }
可持續化,具體來說就是可以訪問歷史版本,以實現跨時間的操作。
怎么實現呢? 試想我們修改線段樹的時候,修改前是舊版本,修改后是新版本,想想新版本和舊版本有什么區別? 區別就在于從根節點到被修改點的路徑上的點改變了,其他的點是不變的
那么這個時候我們新建一個根節點,對于不變的兒子指向舊版本的兒子,對于有變化的兒子就新建一個節點 這樣一來我們有每一個時刻的根節點,就可以訪問到每個時刻的線段樹 空間會不會暴呢? 每條路徑只有logn個點,m次修改我們只多了mlogn個點,可以承受 但實在空間卡得很緊時就要用指針了
比如洛谷P3931
下面給出指針版題解代碼【并非完全是普遍的寫法,由題簡化】: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define LL long long int #define REP(i,n) for (int i = 1; i <= (n); i++) #define fo(i,x,y) for (int i = (x); i <= (y); i++) #define Redge(u) for (int k = head[u]; k != -1; k = edge[k].next) using namespace std; const int maxn = 1000005,maxm = 10000005,INF = 1000000000;inline int read(){int out = 0,flag = 1;char c = getchar();while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}while (c >= 48 && c <= 57) {out = out * 10 + c - 48; c = getchar();}return out * flag; }int N,M,A[maxn];struct node{int v;node *ls,*rs;node() {ls = rs = NULL;} }; node* rt[maxn]; int rti = 0,pos;void Build(node **u,int l,int r){*u = new node;if (l == r){(*u)->v = A[l];}else {int mid = l + r >> 1;Build(&(*u)->ls,l,mid);Build(&(*u)->rs,mid + 1,r);} }void change(node *pre,node **u,int l,int r,int v){(*u) = new node;*(*u) = *pre;if (l == r){(*u)->v = v;}else {int mid = l + r >> 1;if (mid >= pos) change(pre->ls,&(*u)->ls,l,mid,v);else change(pre->rs,&(*u)->rs,mid + 1,r,v);} }int Query(node *u,int l,int r){if (l == r) return u->v;else {int mid = l + r >> 1;if (mid >= pos) return Query(u->ls,l,mid);else return Query(u->rs,mid + 1,r);} }int main() {N = read();M = read();REP(i,N) A[i] = read();Build(&rt[0],1,N);int pre,cmd,v;while (M--){pre = read();cmd = read();pos = read();if (cmd & 1){v = read();change(rt[pre],&rt[++rti],1,N,v);}else {printf("%d\n",Query(rt[++rti] = rt[pre],1,N));}}return 0; }
轉載于:https://www.cnblogs.com/Mychael/p/8282846.html
總結
- 上一篇: Echarts作图之柏拉图
- 下一篇: Echarts与Highcharts的比