• 析合树学习笔记


    先放dalao的blog link

    定义

    对于一个排列定义一个值域连续段

    为一个子区间([l,r])

    满足(max(a[l]...a[r])-min(a[l]...a[r])=r-l)

    即把这个子区间排好序它们是连续的((r-l+1))个正整数

    那么我们容易发现一些性质

    对于两个连续段,它们的交一定是一个连续段

    对于两个有交的连续段,它们的并一定是一个连续段

    我们定义一个连续段是本原连续段,当且仅当满足不存在和它相交却没有包含关系的连续段

    容易发现这样的连续段构成了一棵树形结构

    接下来我们定义树上的析点和合点

    如果把树上一个点x的所有儿子区间([l_1,r_1],[l_2,l_2]...[l_k,r_k])拿出来(这里是下标区间)

    这些子区间是一个一个的连续段

    把他们按照在序列内的顺序排好序(即(l_{i+1}=r_i+1)

    那么如果满足(max(a[l_i]...a[r_i]) le min(a[l_{i+1}]...a[r_{i+1}]))

    即这些子区间对应的值域是连续的

    我们就认为x是合点

    否则x是析点

    在此基础上

    我们可以发现

    合点的任意大于一个在下标上连续的儿子他们拼起来依然是连续段

    析点的任意大于一个在下标上连续的儿子他们拼起来,除了所有儿子拼起来以外,其它的都不是连续段

    第一点非常显然

    第二点可以考虑反证法

    假设存在我们取最长的一段

    那么我们发现不存在与它相交且不包含的值域连续段(不然就不是最长的了)

    那么这一段应该是一个本原连续段

    这与这是几个不同的点矛盾

    进而我们可以得到

    析合树可以构成所有连续段,且所有连续段在析合树上一定是以下两种形式:

    ​1.树节点构成的连续段

    ​2.合点的非平凡儿子区间构成的连续段

    同样使用反证法可以证明

    构造

    基本方法:

    考虑增量,假设做到i,已经得到了([1,i-1])的析合森林,用一个栈维护这个析合森林

    用下面的方法进行增量:

    设置一个当前点now,初始为([i,i]),考虑栈顶的点为top

    1.如果top是合点,并且now可以作为它的最后一个儿子,那么将now设置为top的最后一个儿子,弹出top为新的now,重复这个过程

    2.否则从top开始往回找到最少的栈顶的若干个点可以和now组成连续段,弹出他们,新加一个点向这些点和原来的now连边,将now设置为新点,重复这个过程

    这里注意2里如果只需要找栈中的一个点那么新加的这个点是个合点,否则是析点

    直到栈为空或2找不到

    复杂度分析:

    1是(O(n))的,在2中被合并掉的点是(O(n))的,但是如果到最后都无法合并,复杂度可能会退化到(O(n^2)),所以需要一个(L_i)表示以i为右端点的连续段的最远左端点,超过了就break就(O(n))

    如何求(L_i)

    线段树对(r)动态维护(max(l,r)−min(l,r)−(r−l))

    他的最小值一定是0

    线段树上取最前面的满足最小值是0的l即可

    max和min可以利用单调栈维护

    例题

    CodeChef March Challenge 2020 Good Subsequences

    建出析合树利用连续段的性质

    发现可能的一对一定都是析合树上某个合点的一段连续的儿子区间

    建出析合树随便算算即可

    code

    Comet OJ - Contest #6 permutation

    数析合树可还行

    自己的题解在这里link

    code

  • 相关阅读:
    事务传播机制,搞懂。
    洛谷 P1553 数字反转(升级版) 题解
    洛谷 P1200 [USACO1.1]你的飞碟在这儿Your Ride Is Here 题解
    洛谷 P1055 ISBN号码 题解
    洛谷 P2141 珠心算测验 题解
    洛谷 P1047 校门外的树 题解
    洛谷 P1980 计数问题 题解
    洛谷 P1008 三连击 题解
    HDU 1013 题解
    HDU 1012 题解
  • 原文地址:https://www.cnblogs.com/deaf/p/13473162.html
Copyright © 2020-2023  润新知