SP186 LITELANG - The lightest language 题解
给定
我们发现这个等价于在一棵无限大的 Trie 树上找到
一个简单的想法是每次选择最浅的叶子,将其视作非叶子节点,把
你会发现这个显然是错的。因为你可以直接令
但是我们在这个时候可以继续贪心,继续分裂最小的叶子,插入其所有的叶子,每次选择前
重复进行
我们现在考虑证明一下。首先直接考虑叶子比较难考虑,我们不妨来考虑非叶子(树的内部节点)有几个。
下面不妨考虑权值数组
引理
:令 表示在所有有 个内部节点的解中的最优解,则 的所有内部节点一定是深度最小的 个。给第 小的节点编号 ,相同大小顺序随意,但是保证编号唯一。
证明考虑比较选择深度最小的
引理
:选择了最小的 个节点之后,选择代价最小的 个叶子就是 的答案,如果有多个相等的,那么我们就取父节点编号更小的。这样构造出来的树是唯一的。
原因和上面一样,你可以把大的叶子剪下来拼到小的叶子上。
引理
:最优解是 中的一个。
显然。考虑反证,数一数内部节点个数,替换成
我们第
引理
:在 的前提下,定义合法树为:所有的 个钦定为内部节点的点至少有两个儿子的树。则在合法树中一定存在一个最优解。
这个也比较显然。对于一个没有儿子的被钦定为内部节点的点,把这个点取消钦定为内部节点会得到一样的解法。对于一个只有一个儿子的内部节点,我们把这个儿子直接接到当前点上深度变小,答案更优。
引理
:最优解最多不超过 个内部节点。
因为
因此我们的算法是正确的。
此时还有一个小优化可以把复杂度优化成
引理
:考虑编号为 的节点的第 个叶子,如果 ,那么这个叶子永远不会是答案。
因为内部节点不超过
考虑这样一张无限大表格辅助理解,假设
对应的是这张图,之后的节点就不画了:

枚举行号,可以发现,候选节点一共是
因而只需要考虑
之后还有一些优化。其中一个优化就是如果某次分裂之后权值增加了,那么算法就可以立刻停止了。我们只需要证明答案关于
证明比较复杂。一个感性的理解是,之后想要替换掉原先前
引理
:在有至少 个叶子之后, 权值单谷。
下面我们将分成若干步来证明。
引理
:满足 是合法树的 是一个区间。
显然区间左端点就是第一次有
我们只需要证明,
首先考虑使得
此时增加了一个内部节点
此时
下面我们将这个区间记作
引理
:对于 的 ,其权值单调递增。
考虑此时
考虑是谁替代了
- 是
下面的第一个叶子。此时显然答案会增加。 - 不是
的叶子。根据我们 的定义可以得知,这些叶子权值均大于 中点的权值。因此答案也会增加。
引理
: , 权值单谷,并且下凸。
不妨考虑从
首先,假设
因此,我们分两步构造出
- 选择节点
,钦定 为内部节点,并将 的最小叶子加入 。这个操作会使得代价增加固定的 。 - 从小到大考虑
剩下的所有叶子 。找到 中深度最大的叶子,如果其深度大于 ,用 替换它。
请注意操作
因此每次的代价就是
接下来我们考虑
考虑
考虑第二个操作。假设
因为替换了
如果现在考虑从
- 如果
,那么此时不仅替换的代价增加了,第 大元素还减少了,因此这部分的代价肯定是小于 替换 中的第 大元素的。 - 如果
,这显然不可能发生。因为 深度比 小还没有替换掉,因此 也不可能替换掉。之前替换的叶子 的深度显然也小于 。
因此,你的变化是单调递减的,因此自然也是单谷底。一旦答案的变化为正了,此时就可以直接停止了。
因此我们的算法是正确的。
此外还有一些卡常技巧。
我们不暴力加入所有
注意到某个叶子在父节点被钦定为内部节点的时候如果没能替换掉原来的叶子,那么这个节点在之后就没有用了。
考虑在构造的时候这个叶子何时会加入。唯一会加入的时候是,在当前钦定为内部节点的
但是此时不论是拿这个叶子替换还是直接拿最小的儿子替换,代价显然会增加,算法会在这里直接停止。
另外一个技巧就是,假设权值很不平均,一开始加入的节点会很快被替换出去,这样是很浪费的。
考虑我们有哪几种方式新增一片叶子。
- 加入一个父亲已经钦定为内部节点,但是当前节点尚未加入成为叶子的节点。
- 将一个叶子钦定为内部节点,加入第一个和第二个儿子。
第一种的代价是
具体的,我们分开维护当前代价最小的操作 std::set 维护当前的叶子即可。
但是进行一次操作
不难发现,假设最终进行了
另外一方面,在至少有
最后我们再切换回原算法继续跑得出正确答案。
注意加入这个优化之后其实并不是很容易跑满,实际上
此时还有一个优化,可以将
从引理
在我们证明了单峰性之后,还有一种做法,就是你直接三分,找到这个谷底。实际上整数上的三分可能会遇到一段平坦区间的问题,但是我们在刚刚已经证明了顶点前权值严格递减,顶点后权值严格递增,因此我们只需要二分找到最后一个差分非正的位置即可。
check 的时候依旧考虑我们的那张表格,假设现在要求
后续可以使用分组二分的技巧优化到
进一步的,注意到大多数时间都浪费在了二分的 check 上面了,我们可以考虑用类似单次 check 的多路归并的做法先把前
二分先求出来最优解在哪一组,复杂度是
此时这一组父亲节点中只有
Choi 和 Golin 在论文《Lopsided Trees: Analyses, Algorithms, and Applications》更是将复杂度做到了
上述做法便可以通过加强版数据。
参考资料
- 不知道哪里来的题解。
- 不知道哪里来的另一篇题解。
- 论文《PREFIX CODES: EQUIPROBABLE WORDS, UNEQUAL LETTER COSTS》
如果你知道题解的来源的话,请提醒我标注,感谢。