1. 程式人生 > >主席樹模板(轉)~

主席樹模板(轉)~

#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