loading

线段树合并 草率记录

简单来说,当答案的维护非常不好做,但是对于单个结点能容易用线段树去维护时,就可以考虑线段树合并。

具体算法流程是,从根节点往下遍历线段树结点,如果两棵树中该点的左儿子有一个是空,那么直接把新的左儿子变成非空的那个就行,如果两个都是空的那个新左儿子就还是空的。右儿子同理。这个东西的复杂度依赖于有效结点数,若总操作次数是 \(n\),那么线段树合并的复杂度就是 \(O(n\log n)\),通常合并的都是动态开点的线段树。

如果你只需要支持单点修改,那么线段树合并到这就结束了。但实际做题中还有一些需要你区间修改的线段树合并题,可以标记永久化,也可以使用常规 pushdown tag。

标记永久化(经过实际测试)

每个结点记一个标记,但是我们不进行下传,该结点维护的答案也应该是【只考虑子树内的操作的答案】,注意 pushup 的时候需要写 ans[p]=merge(ans[lp],ans[rp])+tag[p]

其实和普通的标记永久化一样。

常规标记(应该是对的,但没写过)

重点在于合并过程中该如何下传标记。显然每遍历一个结点就下传标记会导致大量结点的产生,使得复杂度退化。

当然该下传还是得下传,要不然线段树就错了。但是我们要避免对 pushdown 过程中新建的点进行合并。

考虑如下做法:我们先提前处理合并后该结点是否有左儿子和右儿子。如果都没有直接退出即可;如果都有直接下放即可;如果只有一个那么还是直接下放,但是只递归有的那一侧,无的那一侧不递归。

两种写法一般都只在叶子结点合并两棵树的信息,非叶子结点使用 pushup 更新。可能存在其他写法。

posted @ 2025-06-25 17:20  dcytrl  阅读(19)  评论(1)    收藏  举报