主席樹模板(轉)~
阿新 • • 發佈:2018-12-16
#include <iostream> #include <cstdio> using namespace std; const int MAX = 20000010; struct Persistable_Segment_tree { int ls,rs,v;//分別是該位置代表的節點或區間的左兒子右兒子和權值 } tr[MAX]; int edit[1 << 20] = {1},w[1 << 20],tot;//edit儲存節點版本 注意第一個(下標0)賦值為1表第一個版本 w儲存點初始權值 int build(int l,int r) {//此處不能直接用++tot代替pos 因為跳轉到子程式中繼續搜尋下去 tot值會增加 而此處值代表當前節點編號 應不變 int pos = ++tot;//tot是當前陣列末尾的位置 ++tot則是在末尾處新建儲存節點或區間的相關資訊 充分利用空間OwO真是太厲害了 if (l == r)//區間左右相等 即只包括一個點 則只存點權 { tr[pos].v = w[l];//記錄初始點權 return pos;//pos是當前節點的編號 需返回 用以讓其父親節點記錄他 } int mid = (l + r) >> 1;//二分存中點 tr[pos].ls = build(l,mid);//記錄當前節點的左兒子編號 tr[pos].rs = build(++mid,r);//記錄當前節點的右兒子編號 return pos;//返回當前節點編號 需返回 用以讓其父親節點記錄他 } int update(int ed,int l,int r,int p,int k)//在ed版本的基礎上 修改p點權值為k 記錄當前區間最左&&最右端的點l&&r {//此處不能直接++tot代替pos 因為跳轉到子程式中繼續搜尋下去 tot值會增加 而此處值代表當前節點編號 應不變 int pos = ++tot;//記錄當前節點編號 充分利用空間OwO真是太奇妙了 if (l == r)//當搜尋到單個節點了 { tr[pos].v = k;//記錄修改後節點權值 return pos;//返回當前節點編號 讓當前版本的父親記錄他 } tr[pos].ls = tr[ed].ls;//將之前的該節點左兒子複製 (引用-->「把子節點指向前驅節點以備複用」) tr[pos].rs = tr[ed].rs;//將之前的該節點右兒子複製 因為之後只會改變兩兒子之一的值 這樣子可以確定該節點位置 int mid = (l + r) >> 1;//二分存中點 if (p <= mid) tr[pos].ls = update(tr[ed].ls,l,mid,p,k);//向下尋找 逼近p點 更改pos點的左兒子 else tr[pos].rs = update(tr[ed].rs,++mid,r,p,k);//向下尋找 逼近p點 更改pos點的右兒子 用tr[ed]的原因是此時tr[pos]只有1深度的孩子的值 return pos;//返回pos pos作為該點父親的某個兒子的位置 用以記錄 } int found(int ed,int l,int r,int p) {//ed是 某版本 儲存區間1~n的值 的位置 if (l == r) return tr[ed].v;//找到該點 此時ed已經變為 記錄當前版本的p點的位置了 其v則是當前版本的p點的權值 返回 int mid = (l + r) >> 1; if (p <= mid) return found(tr[ed].ls,l,mid,p);//向下尋找 逼近p點 ed變為ed的左兒子 else return found(tr[ed].rs,++mid,r,p);//向下尋找 逼近p點 ed變為ed的右兒子 } int main() { int n,m,edition,mode,node,weight;//恪盡職守的變數定義 scanf("%d%d",&n,&m);//發人深省的範圍輸入 for (int a = 1 ; a <= n ; a ++) scanf("%d",&w[a]);//循規蹈矩的節點輸入 build(1,n);//建樹 從區間 1 ~ n 開始遞迴 找左右兒子 for (int a = 1 ; a <= m ; a ++)//循序漸進的命令處理 { scanf("%d%d%d",&edition,&mode,&node);//五花八門的命令輸入 if (mode % 2)//巧妙絕倫的判斷 { scanf("%d",&weight);//撲朔迷離的補充輸入 edit[a] = update(edit[edition],1,n,node,weight);//update解釋見子程式 }//以update此時求出tr陣列的末尾 edit[a]意為在第a個版本時修改的點為edit[a-1]到edit[a]的點(上面那行程式讓本人想了很久很久) else//機智無比的轉折 { edit[a] = edit[edition];//因為複製沒有建立新節點 因此當前版本的所有點等於當前版本(不是第a-1的版本)之前的所有點 printf("%d\n",found(edit[edition],1,n,node));//輸出查詢某edition的某node的值 } } return 0;//逢考必備的結尾 }
轉載來源: https://blog.csdn.net/Frocean/article/details/80888718