线段树合并

前置知识:权值线段树 动态开点

一些题目中我们需要在有限复杂度内合并两棵线段树的信息,此时我们便需要线段树合并/分裂

(后文代码中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 ;
}
posted @ 2025-05-30 21:03  he_qwq  阅读(37)  评论(0)    收藏  举报