主席树介绍:
主席树又被称为持久化线段树,也就是某一时刻对线段树的操作可以被保存下来,后面可以随时在以前的版本上进行查询和修改,这种持久化思想不仅可以用于线段树,还可以用于并查集和平衡树等等。
主席树原理:
需要查询以前的某一个版本,那么我们肯定需要对每一次修改都保存下来。但是如果将整棵树都保存下来,需要的时间和空间成本都太高。所以我们选择保存发生变化的节点:
当修改操作新衍生出一个版本,只需要新建根节点和发生变化的节点即可。
这个时候我们只需要再建立一个数组,保存每个版本的根节点,就可以实现在任意版本上进行查询和修改。
主席树实现:
建树:
struct Node{
int l;
int r;
int val;
}node[N];
int tot=0;
int root[N];
int root_sum=0;
int a[N]; //用于保存初始建树的值
//递归建树,返回根节点索引
int build(int l,int r){
if(l==r){
tot++;
node[tot].l = node[tot].r = 0;
node[tot].val = a[l];
return tot;
}
int now = ++tot;
int mid = (l+r)>>1;
node[now].l = build(l,mid); //递归建立左子树
node[now].r = build(mid+1,r); //递归建立右子树
node[now].val = node[node[now].l].val + node[node[now].r].val;
return now;
}
修改:
//在p版本的基础上修改主席树k号元素为e,并衍生出一个新版本
int change(int p,int l,int r,int k,int e){
if(l==r){
tot++;
node[tot].l = node[tot].r = 0;
node[tot].val = e;
return tot;
}
int now = ++tot;
int mid = (l+r)>>1;
node[now].l = node[p].l; //先复制上一个版本的子节点
node[now].r = node[p].r;
//只修改变化的子节点
if(k<=mid) node[now].l = change(node[p].l,l,mid,k,e);
else node[now].r = change(node[p].r,mid+1,r,k,e);
node[now].val = node[node[now].l].val + node[node[now].r].val;
return now;
}
查询:
//查询以p为根节点版本的k号元素
int find(int p,int l,int r,int k){
if(l==r) return node[p].val;
int mid = (l+r)>>1;
if(k<=mid) return find(node[p].l,l,mid,k);
else return find(node[p].r,mid+1,r,k);
}