首页 > 其他分享 >动态树与 LCT

动态树与 LCT

时间:2024-04-21 21:44:50浏览次数:15  
标签:splay LCT 实链 原树 Splay 虚边 动态

前面所提到与树有关的数据结构,大部分都是在一棵树上进行的。如果是在森林中连边和删边,就要使用 LCT 了。LCT 可以看作是树链剖分与 Splay 树的组合,建模中用到了树链剖分,但实际写起来与树链剖分没什么关系,主要用 Splay 树。它的平摊时间复杂度为 \(\mathcal{O}(\log N)\)。

1 LCT 的思想

  1. 把树分为多条链。在一条链中,边用实链存储(儿子指向父亲,父亲指向儿子),在不同的链中,边用虚边存储(儿子指向父亲,父亲不指向儿子)。这样在一条链中,只维护了这条链的信息(由于父亲不指向儿子,在 push_up 时不会更新儿子的信息,也就只维护了这条链的信息)。在实际操作中,如果要对一条链(两点之间的路径)进行操作,则只需要将这条链分裂出来即可。

  2. 将原树转换为辅助树。这个转换非常精妙。将原树中的实链与虚边提取出来,形成一个新的辅助树。这棵树看起来与原树差别很大,但从其中可以还原出原树的路径特征。因此我们只需要处理辅助树即可。

  3. 实链与 Splay 树。实链是动态变化的,使用 Splay 处理实链,主要是因为 Splay 有“提根”与改善树的形态功能。

下面介绍 LCT 的建模步骤与操作。LCT 的概念很多,操作比较复杂,读者可以多看几遍,加深理解。

2 从原树到辅助树

  1. 将原树剖分为实链和虚边

和树链剖分差不多,每个结点只能向它的儿子中连一条实链。所有非实链的边都是虚边。LCT 对实链和虚边的选择没有要求,整棵树全为虚边也是没问题的。下面是一种剖分的方法。

原树:

剖分后:

  1. 将原树转换为辅助树

首先是实链的转换。每条实链都是一条路径,深度递增。将每个点的深度看作权值,建一棵 Splay 树。在这棵树上进行中序遍历,便能得到原来的链。

下面是一种可以的转换方法。

原树上将同一条链上的结点圈起来:

将链转换为 Splay 树:

每棵 Splay 树旋转并不会改变中序遍历,也就不会改变链的形态,从而保持了原树的特征。

可以发现,在上图中,原来 \(3 \to 1\) 的虚边转化成了 \(6 \to 1\) 的虚边。这样会影响 LCT 的形态吗?答案是不会。在原树中,连接虚边的是一条链的链头,比如上图中,\(3-6-9\) 这条链的链头 \(3\) 有一条虚边。在辅助树中,如果一个点连接了虚边,则代表这个点所在的 Splay 树中,深度最低的点连接了虚边。如上图,\(6 \to 1\) 这条虚边实际上是 \(6\) 所在的 \(Splay\) 中深度最低的点 \(3\) 连接的。

这样一棵辅助树就建成了。

3 LCT 的操作

我们已经得到了辅助树了,那应该如何实现连边和断边操作呢?

下面介绍 LCT 的基本操作。在阅读时,要注意区分是在连通块中操作,还是在一条链中操作;是在原树中操作,还是在辅助树中操作。

1 splay()access()

在这些操作中,最根本的操作是 splay()access()splay(u) 的功能是将 \(u\) 旋转到这条链(不是连通块)的最顶端。access(u) 的功能是将 \(u\) 与该连通块中深度最低的点连一条实链。下面讲解一下 access() 的实现过程。

如果在上图中执行 access(7),则执行的过程如下:

  1. 初始的辅助树:

  1. splay(7),得到:

  1. 将实链 \(7-10\) 改为虚边,使 \(7\) 成为实链中最底端的点:

  1. splay(6)。\(6\) 已经是该链深度最小的点,树不变。

  2. 将实链 \(6-9\) 改为虚边,将 \(6\) 的右儿子改为 \(7\) 并连实链:

  1. splay(1),得到:

  1. 将实链 \(1-2\) 改为虚边,将 \(1\) 的右儿子改为 \(6\) 并连实链:

这样 \(1,3,6,7\) 形成了一棵 Splay 树,中序遍历得到 \(1,3,6,7\),这正是在原树中的顺序。可以看出,LCT 可以随意改变实链与虚边,十分灵活。

access() 函数是由 splay() 函数实现的,时间复杂度为 \(\mathcal{O}(\log N)\)。

2 make_root()

该函数的作用是将 \(u\) 旋转到原树中的根。上面的操作都在辅助树中进行,该函数可以改变原树的形态,是 LCT 的重要技巧。

在原树中,如果要查询 \(2-1-3\) 这条路径,会发现这条路径中结点的深度不是递增的,无法在辅助树中处理这条链。但如果把 \(2\) 旋转到根节点,那么路径就是递增的了。

旋转前:

旋转后:

该函数分为 \(3\) 步:

access(u) \(\to\) splay(u) \(\to\) tag(u),其中 tag(u) 表示打区间翻转的标记。

access(u) \(\to\) splay(u) 执行完后,\(u\) 变为辅助树中的根节点,且为它与原来根节点所连接的实链中,深度最大的点。此时我们执行区间翻转,则 \(u\) 就变成了实链中深度最小的点。

标签:splay,LCT,实链,原树,Splay,虚边,动态
From: https://www.cnblogs.com/lrxmg139/p/18149555

相关文章

  • PT Application Inspector 4.5 (Linux) - 静态、动态和交互式应用程序安全测试
    PTApplicationInspector4.5(Linux)-静态、动态和交互式应用程序安全测试唯一一款提供高质量分析和便捷工具以自动确认漏洞的源代码分析器请访问原文链接:PTApplicationInspector4.5(Linux)-静态、动态和交互式应用程序安全测试,查看最新版。原创作品,转载请保留出处。......
  • 人形机器人 —— NVIDIA公司给出的操作算法(动态操作任务,dynamic manipulation tasks)(机
    原文:https://developer.nvidia.com/isaac/manipulator#foundation-modelsNVIDIA公司准备针对人形机器人的各部分操作分别推出一个AI框架,如:步态控制、3D感知、抓取操作、避障和规划,等等,本文介绍的就是NVIDIA计划推出的操作任务的算法的AI框架(manipulationtasks)。......
  • Linux共享库、静态库、动态库详解
    1.介绍       使用GNU的工具我们如何在Linux下创建自己的程序函数库?一个“程序函数库”简单的说就是一个文件包含了一些编译好的代码和数据,这些编译好的代码和数据可以在事后供其他的程序使用。程序函数库可以使整个程序更加模块化,更容易重新编译,而且更方便升级。 ......
  • 洛谷题单指南-动态规划1-P1077 [NOIP2012 普及组] 摆花
    原题链接:https://www.luogu.com.cn/problem/P1077题意解读:n种花选m个的选法,每种花数量为ai。解题思路:设dp[i][j]表示前i种花选j个的选法对于第i种花,可以选0,1,2...min(ai,j)个则有递推式:dp[i][j]=∑dp[i-1][j-k],k取0,1,2...min(ai,j)初始化dp[0][0]=1100分代码:#incl......
  • H3C 5120配置链路聚合(动态LACP)
    网络拓扑图如下: 组网需求【华三默认链路聚合模式是静态模式】:DeviceA与DeviceB通过各自的以太网端口GigabitEthernet1/0/1~GigabitEthernet1/0/3相互连接在DeviceA和DeviceB上分别配置链路聚合组,并使两端的VLAN10和VLAN20之间分别互通(1)        配置Device......
  • 洛谷题单指南-动态规划1-P1049 [NOIP2001 普及组] 装箱问题
    原题链接:https://www.luogu.com.cn/problem/P1049题意解读:装尽可能多的物品,使得总体积越大越好,即剩余空间最小,还是一个01背包问题,物品的体积就是其价值。解题思路:01背包模版题,物品体积、价值相同,直接采用一维dp。100分代码:#include<bits/stdc++.h>usingnamespacestd;co......
  • 动态规划的引入
    动态规划的引入引言​ 可以将动态规划的核心理解为"记录"和"自底向上"对于一个问题,如果这个问题能够拆解为同类型的有限个子问题,并且最终的结果能够由这些子问题得到,则可以使用动态规划。动态规划的实现思想为"自底向上",即先求出最小子问题的结果,然后通过这个结......
  • @RefreshScope实现动态刷新配置原理
    1@RefreshScope介绍在介绍@RefreshScope之前,先介绍作用域的概念:在springioc中存在5种BeanScope,即:singleton:每一个SpringIoC容器都拥有唯一的一个实例对象(默认作用域)prototype:一个BeanDefinition对应多个对象实例,每次取出的都是不同的对象request:每一个HTTP请求都有自己的B......
  • java动态代理模式
    Java动态代理模式是Java编程语言中的一种设计模式,它提供了一种在运行时动态创建代理对象的方式。这个模式主要用于实现AOP(面向切面编程)的概念,允许开发者在不修改原有业务逻辑代码的情况下,增加额外的功能,如日志记录、事务管理、权限验证等。在Java中,动态代理模式主要依赖于java.l......
  • Trino418版本动态加载catalog不需要重启集群修改思路及实现2
       原来没事的时候改了一个这样的功能,当时也没有仔细研究,后来也没继续弄。详细可以参考 https://www.cnblogs.com/liuzx8888/p/17635913.html当时有1个问题:新增数据源需要每一个节点都去调取API注册,这样非常麻烦,最近闲下来又研究了一下,在原先的基础上做了一些改造。具体流......