线段树合并
前置知识:权值线段树 动态开点
一些题目中我们需要在有限复杂度内合并两棵线段树的信息,此时我们便需要线段树合并/分裂
(后文代码中son存储的是左右孩子节点编号)
part1:合并
假设现在我们遍历到两棵树上相对应的x和y节点
若其中一个为空节点 那我们直接返回另一个节点就行了
若两个都不为空 那么将权值相加 向下遍历并删掉不需要的节点
最后更新节点回退即可
点击查看代码
int merge(int x,int y) {
if(!x||!y) return x+y;
val[x]+=val[y];
son[x][0]=merge(son[x][0],son[y][0]);
son[x][1]=merge(son[x][1],son[y][1]);
del(y);
return x;
}
part2:分裂
将集合中前k小的元素分裂成单独一个集合
注意到如果让分裂区间[l,r]的元素,我们可以先将[1,l-1]分出来,再把[l,r]和[r+1,n]分出来,最后将[1,l-1]和[r+1,n]合并
现在我们来看分裂过程
假设我们现在要把这棵树分裂成以x和以y为根的两棵树 前k小在x的树里 剩下的扔给y 由于y最初不确定 直接传引用
我们考虑在x节点 如果它左孩子权值比k要小 显然右子树中还有节点属于x 所以我们需要遍历右孩子
如果左孩子权值等于k 直接左归x右归y
如果大于k呢 那么右子树肯定全归y了 递归左子树
点击查看代码
void split(int x,int &y,int k) {//分裂
if(x==0) {
return ;
}
y=newnod();
int v=val[son[x][0]];
if(k>v) split(son[x][1],son[y][1],k-v);
else swap(son[x][1],son[y][1]);//右子树全归y
if(k<v) split(son[x][0],son[y][0],k);
val[y]=val[x]-k;
val[x]=k;
return ;
}

浙公网安备 33010602011771号