主席树

主席树

思路

主席树是可持久化线段树,换而言之就是可以记录历史版本。

首先想到每进行一次操作就重新新建一棵树,然而这样空间复杂度是巨大的,且有很多并未改变的点重新建立就被浪费了。因此考虑每次只重建被改变的点,对于单点修改而言也就是修改了叶节点到根节点上一条链的信息,空间复杂度就极大减小了。

构建一个主席树,不同于线段树的点是左儿子编号\(u<<1\),右儿子编号\(u<<|1\),主席树点是连续标号的,即左儿子总是\(u+1\)。每进行一次添加操作,就将原版本根节点数据复制到新的版本,相同节点就不改变,直接连接到原来的节点,不同的节点就重现建立,连接到新的版本。每次记录每个版本的根节点编号为\(root\),查找时从\(root[u]\)开始查找第\(u\)个版本。

空间复杂度与线段树相同,为\(n\log n\)

空间复杂度亦为节点数的将会是\(n+m\)

Code

#include<bits/stdc++.h>
using namespace std;

int root[1000005], a[1000005], n, m;					//root记录第i版本的根节点编号 
int v, op, pos, val;
int sum = 0, tot = 1;									//tot记录当前产生新节点的下标,sum记录根的下标 

struct SegmentTree{
	int l, r;											//左右边界 
	int ls, rs;											//左儿子对应编号,右儿子对应编号 
	int val;											//点权值 
}tree[20000005]; 

template<typename T>
inline void read(T&x){
    x = 0; char q; bool f = 1;
    while(!isdigit(q = getchar()))  if(q == '-')    f = 0;
    while(isdigit(q)){
        x = (x<<1) + (x<<3) + (q^48);
        q = getchar();
    }
    x = f?x:-x;
}

template<typename T>
inline void write(T x){
    if(x < 0){
        putchar('-');
        x = -x;
    }
    if(x > 9)	write(x/10);
    putchar(x%10+'0');
}

inline void build(int u, int l, int r){
	tree[u].l = l, tree[u].r = r;
	if(l == r){
		tree[u].val = a[l];
		return;
	}
	tree[u].ls = ++tot;									//左儿子=当前节点数+1 
	int mid = (l+r)>>1;
	build(tot, l, mid);
	tree[u].rs = ++tot;									//右儿子=当前节点数+1 
	build(tot, mid+1, r);
}

inline void add(int u, int v, int k, int num){
	tree[v] = tree[u];									//当前新建版本复制原版本 
	if(tree[u].l == tree[u].r){							//如果就是更新的点,将val改为num 
		tree[v].val = num;
		return;
	}
	int mid = (tree[u].l+tree[u].r) >> 1;
	if(k <= mid){
		tree[v].ls = ++tot;								//建立一个新的左儿子节点 
		add(tree[u].ls, tot, k, num);
	}
	else{
		tree[v].rs = ++tot;								//建立一个新的右儿子节点 
		add(tree[u].rs, tot, k, num);
	}
}

inline void query(int u, int k){
	if(tree[u].l == tree[u].r){
		write(tree[u].val);
		putchar('\n');
		return;
	}
	int mid = (tree[u].l+tree[u].r) >> 1;
	if(k <= mid)	query(tree[u].ls, k);
	else	query(tree[u].rs, k);
}

int main(){
	read(n), read(m);
	for(register int i = 1; i <= n; ++i)	read(a[i]);
	build(1, 1, n);
	root[0] = 1;										//0号版本根节点是1 
	for(register int i = 1; i <= m; ++i){
		read(v), read(op);
		if(op == 1){
			read(pos), read(val);
			add(root[v], root[++sum]=++tot, pos, val);	//新建一个版本根节点=++tot
		}
		else{
			read(pos);
			query(root[v], pos);
			root[++sum] = root[v];						//新建一个相同版本 
		}
	}
	return 0;
}
posted @ 2024-10-09 18:22  Zzzzzzzm  阅读(12)  评论(0)    收藏  举报