题目大意
给定一棵以 1 号节点为根的树,每个节点有一个整数能量值。要求通过任意重排每个节点的子树顺序,生成字典序最小的先序遍历序列。
题解思路
对任意节点 u,设它的“最稳定子序列” S(u) 是在允许重排 u 的孩子后,u 的子树先序序列中字典序最小的那条。显然整棵树的答案就是 S(1)。
如果 u 的孩子子树最稳定子序列分别是 S(v1),S(v2),…,那么 S(u) 一定形如:
S(u) = [a_u] + S(按某顺序排列的孩子 1) + S(孩子 2) + …
为了让拼接后的整体字典序最小,只需把孩子按各自的 S(v) 的字典序从小到大排序后依次拼接即可:因为先序序列在进入第一个孩子子树前已经固定为 a_u,之后最先产生差异的位置一定落在“第一个不同孩子的子序列”里,所以把最小的子序列放在最前是最优的。
因此问题变成:需要能高效比较两个子树的 S(u) 与 S(v)。直接把 S(u) 展开成数组会导致总长度平方级。
做法是把每个 S(u) 当成一条“序列对象”,用一棵隐式 Treap 维护:
* 长度 len;
* 两个模数下的多项式哈希值;
* 支持 O(log n) 取第 k 个元素、取前缀哈希。
比较两个序列时,用二分 + 前缀哈希求它们的最长公共前缀 LCP,再比较下一位元素即可完成字典序比较。对子树排序后,用 Treap 进行序列拼接(merge)即可得到父节点的序列对象。
整体流程:先把树以 1 为根,得到父子关系与后序顺序;按后序从叶到根处理,每个节点对孩子排序并合并生成自己的序列对象,同时把排序结果存下来。最后按存下来的孩子顺序做一次先序遍历输出能量值。
复杂度:设比较一次序列为 O(log^2 n),则总复杂度约为 O((n + 总排序比较次数)·log^2 n),在本题规模内可通过;空间 O(n)。
参考代码