析合树学习笔记

析合树是一种连续段数据结构。

引入:

给定排列 {Pn},求值域连续的段的个数。

概念

排列 是排列。

连续段 是值域连续的段,满足集合运算。

值域区间 是连续段值域的区间。

本原段 是不与其他连续段部分相交的连续段。

连续段集 Ip本原段集 Mp

形式化地,

定义序列 Pn排列 |P|=ni[1,n],iP

定义 (P,[l,r])连续段 x,z[l,r],y[l,r],Px<Py<Pz

连续段 (P,[l,r]) 满足 [l,r] 上的集合运算。

定义 (P,[l,r])值域区间[mini=lrPi,maxi=lrPi]

定义连续段集 Ip={A|A 是连续段}

定义连续段 X本原段 AIp,XA=(P,)XAAX

定义本原段集 Mp={A|A 是本原段}

析点与合点

析合树的点集是 Mp

概念

节点 u值域区间[ul,ur]

节点 u子节点序列 Suu极长真子本原段序列。

节点 u子节点排列 PuSu 按值域区间左端点离散化的结果。

节点 u合点 当且仅当 Pu 有序。

节点 u析点 当且仅当 u 不是合点。

形式化地,

定义节点 u值域区间[ul,ur]

定义节点 u子节点序列 Su={v|vu,wu,vw},且按 v左端点排序。

注意,v左端点不是 v值域区间左端点

定义节点 u子节点排列 Pu,i=|{vlSu,il|vSu}|

定义节点 u合点 i[1,|Su|],Pu,i=ii[1,|Su|],Pu,i=|Su|i+1

定义节点 u析点 u 不是合点。

性质

u 的子节点的并是 u。显然。

u 是合点当且仅当 Su 的子段构成连续段。显然。

u 是析点当且仅当 Su 的多元素子段不构成连续段。

证明:若命题不成立,则 Su 构成连续段的极长子段构成本原段,而此本原段未在析合树中出现。

形式化地,

vSuv=u

u 是合点 [l,r][1,|Su|],i=lrSu,iIp

u 是析点 [l,r][1,|Su|],l<r,i=lrSu,iIp

建树

可以用 增量法 O(nlogn) 建析合树。

Pi 依次加入析合树,用一个栈维护 Pj|j[1,i] 形成的析合森林。

策略

维护当前 i 所在子树的根 u(初值为 (P,[i,i])),考虑 u 与栈内节点的合并情况。

  1. u 能成为栈顶节点的子节点 栈顶节点是合点且 u 与栈顶节点的最右子节点能合并成连续段,令 u 成为栈顶节点的子节点,然后令 u 栈顶节点。
  2. u 不能成为栈顶节点的子节点,令 u 与栈顶若干节点合并成连续段 v|Sv| 最小。考虑 v 的类型,容易发现此时 v 是合点 |Sv|=2。令 uv
  3. 重复上述方案直到不能进行(即找不到 2. 中合法的 v),将 u 入栈。

(来自 CF,对 {9,1,10,3,2,5,7,6,8,4} 建析合树)

根据上述策略,我们需要快速判断 u 与一些点能否合并成连续段,即判断任意后缀 [j,i]|j[1,i] 是否连续段。

子问题

考虑如何快速判断 [j,i]|j[1,i] 是否连续段。

P 是排列,所以 [j,i] 是连续段当且仅当 maxjkiPkminjkiPk=ij

维护 Qj=maxjkiPkminjkiPki+j|j[1,i],则 [j,i] 是连续段当且仅当 Qj=0

考虑更新 i 时如何快速更新 Q

maxjkiPkminjkiPki+j 中,j 是不变的,每次更新 iQj 的贡献减一,最值的贡献不好维护。

Pi 更新了 Bi 个后缀最值的位置,注意到 i=1nBi=O(n)(单调栈结论)。

用单调栈维护后缀最值的位置,以后缀最小值为例。

维护单调递增的栈 x。不难发现 Pxi 是后缀 [j,i]|j(xi1,xi] 的最小值。

xi 出栈时失去对 Qj|j(xi1,xi] 的贡献,所以 Qj|j(xi1,xi]Qj+Pxi

i 入栈时获得对 Qj|j(xtop,i] 的贡献,所以 Qj|j(xtop,i]QjPi

后缀最大值同理。

转化为区间加,单点查询,找最左 0,线段树维护。

例题

P8600 [蓝桥杯 2013 省 B] 连号区间数 / CF526F Pudding Monsters

给定排列 {Pn},求值域连续的段的个数。

考虑析合树内每个点的贡献。

由合点的性质,若 u 是合点,则 Su 的任意子段构成连续段,即 u 的贡献为 (|Su|2)

由析点的性质,若 u 是析点,则 Su 的任意多元素子段不构成连续段,即 u 的贡献为 1

注意到 |Mp|=O(n),所以时间复杂度 O(nlogn)

代码:指针 1.98KB 199ms 8.48MB非指针 2.02KB 207ms 9.66MB


一些序列上连续段问题可以转化为析合树上问题。

P4747 [CERC2017]Intrinsic Interval

给定排列 {Pn}m 次询问包含某区间的最短连续段。

只讨论在线做法。

考虑对询问区间 [l,r],求出 (P,[l,l]),(P,[r,r]) 在析合树上的 LCA 为 u 点。

由合点的性质,若 u 是合点,则 Su 的任意子段构成连续段,Su 上二分包含 [l,r] 的最短子段即可。

由析点的性质,若 u 是析点,则 Su 的任意多元素子段不构成连续段,即答案为 u

代码:非指针 2.79KB 2.65s 33.56MB


没了的 JDOI G LAOI R1 没了的 T4 LAOI 没了的入团赛 T4 无中生题

给定排列 {Pn}m 次询问包含某区间的连续段数量。

这题比较饱经风霜,所以题解比较详细(

考虑对询问区间 [x,y],求出 (P,[x,x]),(P,[y,y]) 在析合树上的 LCA 为 u 点。

容易发现只有 Surootu 路径上的点产生贡献。

考虑 Su 的贡献,即计算有多少 Su子段包含 [x,y]。(u 的贡献算到 rootu 路径上)。

u 是合点则 Su 上二分,若 u 是析点则贡献为 0

u 是合点的情况)

维护树上前缀和数组 vu 表示 rootu 路径的贡献。

c=Su,i,考虑 c 这一层对 vc 的贡献。

u 是合点即统计有多少 Su子段包含 c,跟上面完全一致,就不画图了。

u 是析点则贡献为 1,即 c 本身。

不难得到递推式:

vc={vu+i(|Su|i+1)1u 是合点(考虑包含 c 的真子段数量)vu+1u 是析点(析点的性质)

两部分贡献加起来即可。

代码:非指针 2.94KB 2.30s 140.51MB

众所周知析合树的题都可以不用析合树做,所以如果有人会不用析合树做这个可以私我(


CF997E Good Subsegments

给定排列 {Pn}m 次询问某区间包含的连续段数量。

只讨论在线做法。

相当于区间 CF526F,所以考虑类似想法。

定义析合树内每个点的贡献:

由合点的性质,若 u 是合点,则 Su 的任意子段构成连续段,即 u 的贡献为 (|Su|2)

由析点的性质,若 u 是析点,则 Su 的任意多元素子段不构成连续段,即 u 的贡献为 1

定义 Su 上区间 [l,r] 的价值为其中每个点的子树贡献和之和,若 u 是合点,还要加上 (rl+12)

考虑询问 [x,y] 时,统计 [x,y] 包含的极大 Su 上区间价值和。

(P,[x1,x1])u 点,(P,[y+1,y+1])v 点,l=lca(u,v)

Uudepudepl1 级祖先,Vvdepvdepl1 级祖先。

(黑色线段表示一个节点,红色,绿色线段表示需要统计的区间)

对于红色线段:

对于所有 uU(不含 U)的路径上的点 o,设 oSfao 的第 i 位,则 Sfao 上区间 (i,|Sfao|][x,y] 包含,需要统计其价值。

同理,对于所有 vV(不含 V)的路径上的点 o,设 oSfao 的第 i 位,则 Sfao 上区间 [1,i)[x,y] 包含,需要统计其价值。

定义 u 的点权为 uSfau 上的前 / 后缀(不含 u)价值,则转化为求静态树链点权和,树上前缀和维护之。

对于绿色线段:

USl 的第 L 位,VSl 的第 R 位,则 Sl 上区间 [L+1,R1][x,y] 包含,需要统计其价值。

则转化为求 Sl 区间价值,维护每个 Su 的前缀子树贡献和之和、每个点 uSfau 上的排名即可。

代码:非指针 3.33KB 685ms 51.67MB

比较不理解为什么 7s,放根号过去?

CF526F *3000,这个也 *3000?加个强制在线绝对不止

区间最长连续段维护方法相同,这里不再赘述。

闲话

析合树是可以 O(n) 建树的……但是我不会

所以 CF526F 和 CF997E(Tarjan 求 LCA)都是可以 O(n) 做的,比很多非析合树做法好(

但是看起来代码复杂度会巨大,先咕着。

posted @   Jijidawang  阅读(36)  评论(1编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示