本文绝大部分参考 Naitir - 「技巧总结」第k小和贪心问题,是在此基础上的解说。

符号约定:表示集合

前言

第 k 小(大)的问题定义在全序集合上,相关算法不胜枚举。常用的方法有二分转化为计数问题解决、数据结构直接维护、离线后整体二分等等。

大部分方法都建立在所求答案结构相对简单,亦或是全序结构比较特殊,容易计数的前提下。然对于一类全序结构无特殊性质,只可知前驱后继,甚至连前驱后继都难以判断,只能断言比之优劣的结构,上述算法将难以有效。这也是本文的算法解决的问题。

为了方便大家理解,我们以一个例题引入。

【例题】子集第 K 小问题

有一个长度为正整数序列。对于一个的子集,记表示一个子序列的价值。对于所有个子序列的价值,求其中的第小值,并输出方案。

本题的特点是:全序结构规模过大(),并且结构没有特殊性质,难以计算一个方案的前驱后继。

但是对于子集,我们可以容易地判断某些方案一定是比它大的。例如对于,一定有。换言之对于一些与相关的方案我们可以断言它们与的大小关系。

分析出这些信息后,容易想到维护一个小根堆。初始时将空集加入堆。每次取出堆顶的集合,记作。并将【一部分与相关的比大】的集合都加入堆中。这样做次操作后得到的就是第小的方案。

问题转化为:【一部分与相关的比大】的集合究竟是哪些集合?

为了方便叙述,我们称【一部分与相关的比大】为拉入堆中。称是它们的引子。它们是继子

方法一

对于所有,将加入堆中。

这个方法显然不具备正确性,因为一个集合具有多个引子,会出现重复加入的情况。

方法二

只在子序列末尾加入,即对于中的最大值(对于空集,),考虑所有,将加入堆中。

这个方法具备正确性,因为每个非空集合有且仅有一个引子。

而这个方法的时间复杂度是,空间复杂度。因为一个集合有个继子。

详细的正确性说明

每个集合有且仅有一个引子,说明每个集合一定会在某个时刻被拉入堆中。

而根据小根堆的性质,对于一个集合,只有比他小的集合都被取出过堆顶后,它才会被取出。

因此第次取出的堆顶一定是第小的方案。

换言之,【非初始集合具有唯一引子】的方法都是正确的算法。

方法三

方法二的主要缺陷是继子过多,导致时空复杂度过大。换言之我们需要找到一个引子唯一,继子的偏序结构。

考虑将从小到大排序。

那么对于集合,我们将中某一元素(前提是不在中)得到的集合一定是比大的。

因此我们的方法是对于集合,设其中的最大值是(对于空集,)。若,那么我们就将分别加入堆中。通俗地说,要么我们将子序列最后一个下标加一,要么就加一个数到子序列中。

这个方法显然具备正确性。并且复杂度足够优秀,为

小结

由于全序结构的不明显,因此传统的全序算法在这个问题上难以适用。

上述的算法的本质是利用结构中隐藏的偏序性质(一些与相关的方案我们可以断言它们与的大小关系)组合构建出全序结构,通过小根堆维护以优化复杂度。

简单来说传统的方法是从最小值开始找次后继,找的是一个集合的后继。而堆维护偏序算法干的事情是,找若干个集合的后继,即踢掉最小的,将更多可能的后继加入。

【例题】 扩域版第 K 小问题

给出个正整数集合,第个集合记作,其中的第小的数记作。现在要从每个集合里拿出了一个数,方案的价值是拿出的数的和,求第小的代价是多少。

延续子集第 K 小的思路,考虑用堆维护偏序。

考虑一个选数方案其中表示第个集合选。可以发现,将其中任意一个(可以增大的)加一得到的方案都是比它大的。

那么初始时将加入堆中。同时我们将集合按照从小到大排序。

为了保证引子唯一,我们找到方案中最大靠后的的元素对应的下标(即最大的使得,若没有则):

  • ,将加入堆中。
  • ,将(等价于)加入堆中。排序保证了这一步的正确性。

这样可以保证引子唯一,进而保证算法正确性。时间复杂度

传统算法能否给劲

对于这类问题,其实传统算法思想并非毫无用武之地。

利用传统算法思想我们可以获得复杂度稍劣但更为普遍的解法。

【另解】(扩域)子集第 K 小问题

仍然是子集第 K 小问题,这次我们从二分的角度思考。

二分答案后,问题转化为求权值和小于等于的方案数。

仍考虑用堆维护答案。我们用堆维护前个元素的方案中权值和小于等于的所有方案。

考虑加入,那么我们枚举堆中每一个元素,如果就将加入堆中。

一旦堆的大小超过就停止 check。

这个算法的复杂度视实现情况为或者

上述过程甚至可以改成线段树分治后做堆合并。合并也是按上述方式暴力合并。

对于扩域子集第 K 小问题,按类似的方法做也可以做到

【应用】树上第 K 小连通块

给出一棵个点带正整数点权的树,求其上点权和第小的连通块。

这个问题则只能用二分思想解决。

二分答案后问题转化为连通块计数。将连通块按其中深度最浅的点分类,就转化为树上堆合并的过程了。

QOJ 1548

【应用】购物计划

个商品,具有种类和价值

现在要求购物,第种的物品购买个数要在之间。

求价值和第小的代价。

本题实际上是两个第 K 小问题的套娃。先在同一种类里做,然后对原问题做。

子问题

现在问题转化为大小在范围内的子集第小问题。

不妨将物品按价值排序,假设分别为。为了方便转移我们将末尾的连续下标记作一段区间,可以用表示,其含义是我们选择的物品的末尾的极长下标连续段是,并且是我们选择的第个物品。其他信息可以忽略。初始状态

转移有:

  • ,则有

容易证明引子的唯一性。

可以堆维护。

原问题

原问题即为扩域第 K 小问题。