1 条题解
-
0
完美二叉树 DFS 序合法性判断 题解
问题重述
给定一棵完美二叉树( 个节点,根为 ,节点 的父节点为 ),以及一个排列 表示某个 DFS 遍历顺序。需要回答 个查询,每个查询交换 中的两个位置,并判断当前排列是否可能是某个合法的 DFS 顺序。
DFS 规则:先访问当前节点,然后以任意顺序递归访问子节点。
核心观察
在 DFS 过程中,对于任意节点 :
- 节点 必须出现在其所有后代节点之前。
- 节点 的子节点在遍历顺序中必须连续出现,并且它们所覆盖的区间恰好填满 的整个子树区间(除去 自身)。
设 表示节点 在排列中的位置(即 ), 表示以 为根的子树大小。
对于节点 及其子节点集合 (完美二叉树中 或 ),将子节点按 值从小到大排序得到 ,则合法 DFS 序必须满足:
$$pos[c_{i+1}] = pos[c_i] + siz[c_i], \quad 1 \le i < m $$如果 是叶子节点( 为空),则自动满足条件。
判定条件的等价形式
上述三个条件可以合并为一个简洁的判定:
设 ,,(即最后一个子节点的编号)。
那么条件等价于:
且
在合法情况下,第二个不等式实际上取等号。用 足以判断:若 ,则最后一个子节点的子树超出了 的子树范围,非法。
数据结构设计
为了高效处理交换操作,维护以下信息:
- :排列中第 个位置上的节点编号。
- :节点 在排列中的位置,即 。
- :节点 的子树大小,可以通过一次自底向上遍历计算得到。
- :一个有序集合(如 C++ 中的
set),存储节点 的子节点在排列中的位置,即 $son[x] = \{ pos[c] \mid c \in \text{children}(x) \}$。
使用有序集合是为了快速获取子节点位置的最小值和最大值。
初始化的计算
- 读入 和父节点信息。
- 初始化 ,然后从 到 倒序累加:。
- 读入初始排列 ,同时建立逆映射 。
- 对于每个节点 ,将其子节点的位置插入 中。
- 计算每个节点是否合法,得到初始合法节点总数 。
处理交换操作
每次交换 和 ,记 ,。
受影响节点
交换会影响以下节点的合法性:
- 节点 本身(因为它的位置改变了)
- 节点 本身
- 节点 的父节点 (因为子节点集合变化了)
- 节点 的父节点
注意 和 可能相同,需要去重。
更新步骤
- 将 加入一个集合 。
- 对于 中的每个节点 ,从全局计数 中减去 。
- 从父节点的子节点集合中删除旧位置:
- 交换 与 ,同时交换 与 。
- 将新位置插入父节点的子节点集合:
- 对于 中的每个节点 ,重新计算 并加回 。
- 如果 ,则所有节点都合法,输出
"YES",否则输出"NO"。
复杂度分析
- 每个节点只需要 时间判断合法性。
- 对有序集合的插入、删除、查找最小值和最大值操作都是 。
- 每次交换只更新常数个节点(最多 个)。
- 总时间复杂度:。
- 空间复杂度:。
题目约束 ,,完全可行。
正确性说明
该算法的正确性基于以下事实:
- DFS 序的合法性可以分解为每个节点的子树是否合法,并且这些条件相互独立。
- 每个节点的合法性只依赖于其子节点在排列中的位置关系,以及子树大小。
- 交换操作只会影响被交换的两个节点及其父节点,因此只需要局部更新。
- 通过维护所有节点合法性的计数,可以在 时间内回答每个查询。
- 1
信息
- ID
- 6622
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者