羲和树

简介

析合树是用于处理序列上一类特定结构的数据结构与思想。

这类结构可能具有如下特征:

  • 要么包含,要么不交。

  • 下标和值域的连续性。

如果有题目出现了上述两点特征,就可以往析合树的想法上靠。

概念

段

对于一个 \(n\) 阶排列 \(p\),定义其中的一个段为 \(p\) 的一个非空区间 \([l,r]\),满足 \(p_l \sim p_r\) 所组成的集合在值域上是连续的。

若一个段 \(A\) 满足:不存在其余任何一个段 \(B\),使得 \(B\) 与 \(A\) 有交且不包含。那么称 \(A\) 为排列 \(p\) 的本原(神)连续段。

任意一个长度 \(>1\) 的本原连续段都可以被划分为若干个不交的本原连续段。

所有的本原连续段显然构成了一棵树的结构,这就是所谓的析合树。

合点

在析合树上,若一个节点的所有儿子是有序的,那么我们称这个点是合点。

这里的有序只考虑儿子与儿子之间的相对顺序,而不考虑单个儿子内部的顺序。

例如区间 \([1,5]\) 是一个本源连续段,其值域也为 \([1,5]\),它能够被划分为 \([1,1],[2,4],[5,5]\) 值域也分别为 \([1,1],[2,4],[5,5]\),那么他就是一个合点。

无论儿子 \([2,4]\) 内部的排列是 \(\{4,3,2\}\) 还是 \(\{3,4,2\}\) 都与 \([1,5]\) 是否为合点无关。

显然,若一个点仅有两个儿子,则其必为合点。

析点

同理,若一个点的儿子无序,则该点为析点。

特别地,我们钦定叶子节点是析点。

建立

段的判定

考虑如下过程:从左到右枚举右端点 \(r\),需要实时维护出有哪些左端点 \(l\),使得 \([l,r]\) 是一个段。

区间 \([l,r]\) 是段的充要条件可以写作 \(r-l=\max_{i=l}^r p_i - \min_{i=l}^r p_i\)。

维护以当前右端点为结尾的每个后缀的极值是单调栈的经典运用。

因为显然有 \(\max_{i=l}^r p_i - \min_{i=l}^r p_i+l \geq r\),令 \(f(l)=\max_{i=l}^r p_i - \min_{i=l}^r p_i+l\),我们只需要用线段树维护 \(f\) 的最小值即可。

过程

维护三个栈,其中两个用于计算最值,令一个用来存储当前还没有父节点的段的信息。

从左到右扫描,设当前在点 \(r\),维护 \(now\) 表示当前要处理的段,初始设为 \([r,r]\)。

然后在线段树上查找最小的 \(l\),使得 \([l,r]\) 是一个段。

接下来进行构造:

下面所指的合并只要求整体是段即可,而拼接还要求有序。

  • 若栈顶的点是合点,并且其顺序能与 \(now\) 拼接,那么就把 \(now\) 拼接在栈顶段上,并更新 \(now\) 为栈顶段,弹出栈顶。

  • 否则判断栈顶点能否与 \(now\) 合并,如果可以就弹出栈顶,新建一个合点,把 \(now\) 设为这个合点。

  • 再否则就一直弹栈,直到整体能够合并,新建一个析点,把 \(now\) 设为这个析点。

重复以上判断,直到栈顶的左端点是 \(<l\) 就停止。

最后把 \(now\) 加入栈。

注意,析合树的点数是 \(2n\) 的,数组大小需开两倍。

例题

\(\text{Intrinsic Interval}\)

考虑先建出析合树。

一个合点的儿子要么是析点,要么是反方向的合点。

所以对于查询 \([x,y]\),直接找到 \(x\) 和 \(y\) 的 \(\text{LCA}\),若其为析点,则答案就是 \(\text{LCA}\)。

否则就是 \(x\) 和 \(y\) 所对应的两个儿子的区间。

\(\text{Good Subsegments}\)

建出析合树后,考虑 \(x\) 和 \(y\) 到 \(\text{LCA}\) 的链。

对于链上的合点,记其完全被包含在询问区间内的儿子个数为 \(c\),则其贡献的方案数为 \(\binom c 2\),还要再加上这些儿子各自内部的所有贡献。

若为析点,则只需要加上儿子贡献即可,如果完全包含该析点要额外 \(+1\)。

这道题还有更好想的历史和线段树做法。

posted @ 2026-10-11 18:24  jr_zch  阅读(25)  评论(3)    收藏  举报