【线段树合并】学习笔记

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


我们现在假设有如图所示两棵线段树(省略区间信息):

合并(merge)操作就是将这两棵线段树按某种方式组合成一棵新线段树,并且所有节点权值的组合方式应一致。

首先,我们要清楚,线段树合并本质上是一个相当暴力的过程,我们直接在两棵线段树上跑 DFS,对当前节点左右儿子情况进行分类讨论,再合并节点信息(例如求和、取 \(\max\) 等)即可。如果不考虑记录合并前线段树的信息的话,我们可以直接将新的信息覆盖在其中一棵线段树上,本文以覆盖在第二棵线段树上为例。

我们根据图示看看上图两棵线段树如何合并(黄色节点表示已遍历)。

前几个遍历到的节点中,节点左右儿子情况一致,直接将信息合并:

遍历到了图中红边节点(左边的线段树)时,我们发现右边线段树上对应节点没有左儿子,我们可以直接开一个新点(蓝色节点),该新点储存的信息与红边节点左儿子的信息一致,并将其连在右边线段树上:

继续接着遍历至下图中红边节点,此时与右边线段树上对应节点左右儿子情况比较。因为我们要将信息覆盖到右边线段树上,所以可以不用管左边线段树的空左儿子,只用考虑将其右儿子直接搬到右边即可,方法与上面的一致:

依次类推:

此时右边用紫色矩形框起来的就是合并后的线段树了。

由此我们可以总结出合并时的具体操作情况:

  • 若两树对应节点有一边为空,就直接把一边信息直接复制到另一边;
  • 否则就直接合并信息即可。

对应地可以写出代码:

int merge(int u, int v, int l, int r)//线段树合并 
{
	if(!u) return v;//空儿子 
	if(!v) return u;//同上 
	if(l == r)
	{
//		这里是具体的信息合并处理操作 
		return u;
	}
	int mid = (l + r) / 2;
	tr[u].l = merge(tr[u].l, tr[v].l, l, mid);//继续遍历 
	tr[u].r = merge(tr[u].r, tr[v].r, mid + 1, r);//继续遍历 
	pushup(u);//上传信息 
	return u;
}
posted @ 2025-05-27 17:57  cold_jelly  阅读(35)  评论(0)    收藏  举报