主席树
主席树
思路
主席树是可持久化线段树,换而言之就是可以记录历史版本。
首先想到每进行一次操作就重新新建一棵树,然而这样空间复杂度是巨大的,且有很多并未改变的点重新建立就被浪费了。因此考虑每次只重建被改变的点,对于单点修改而言也就是修改了叶节点到根节点上一条链的信息,空间复杂度就极大减小了。
构建一个主席树,不同于线段树的点是左儿子编号\(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;
}