主席树模板(转)~

#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

猜你喜欢

转载自blog.csdn.net/qq_41286356/article/details/82765548