记一类偏序结构第 K 小问题 | P6646 [CCO 2020] Shopping Plans

有这么一类题目,让你数一个状态空间中第 \(k\) 小的状态,然而状态空间可能特别大以至于无法遍历并排序。

这个时候有一类方法就是利用状态空间中隐含或主动构造的偏序结构,利用偏序边边权非负的性质,在偏序结构构成的图上跑 dijkstra 算法,即可快速得到第 \(k\) 小。

但是在这个过程中可能需要一些技巧性的构造来做到不重不漏。大部分情况情况下需要进一步做到 \(O(1)\) 转移。

具体的,如果我们对每条边 \(u\rightarrow v\),称 \(u\)\(v\) 的继子,\(v\)\(u\) 的引子,那么我们构造出来的偏序结构需要满足以下性质:

  1. \(u\) 偏序 \(v\)。具体的,\(u\) 的权值总是 \(>v\)
  2. 每个状态都有唯一引子。若没有引子则无法遍历整个状态空间,若不唯一则会计重。
  3. 每个状态应该仅拥有 \(O(1)\) 个继子,否则转移复杂度可能错误。

在这个结构上面维护优先队列即可做到大部分 \(\mathcal O(k\log k)\) 的复杂度。


子集和第 \(k\)

给定一个序列,试求其和第 \(k\) 小的子集?

我们可以举出来一些例子:

对于集合 \(S\),我们连边 \(S\rightarrow T\),其中 \(T\) 在二进制表示下 \(=S+1\)

这个算法显然是错误的,其满足性质 2,3 但不满足性质 1。

对于集合 \(S\),对每个 \(e\notin S\),连边 \(S\rightarrow S\cup \set e\)

这个算法是错误的,其不满足每个状态引子唯一。也不满足性质 3。

对于每个集合 \(S\),对每个 \(e>S\) 中最大值,连边 \(S\rightarrow S\cup \set e\)

这个算法也是错误的。其满足性质 1,2,但不满足性质 3。

对于每个集合 \(S\),取 \(q\)\(S\) 中最大值,\(e\)\(>q\) 的最小值。连边 \(S\rightarrow S\cup \set e\)\(s\rightarrow (S\setminus q)\cup\set e\)

这个方案是正确的。我们考虑每个集合 \(T\) 的引子。若 \(T\) 的最大值 \(e\) 的前驱 \(q\in T\),则有 \(T\setminus e\)\(T\) 的引子,否则 \((T\setminus e)\cup \set q\) 即为其引子。即引子存在且唯一。且排序后保证第二种转移增量非负。

个人看法(? 可以发现,我们干的事情类似于将一个菊花摊成一条链(又称三度化),这样能在保证遍历的情况下保证出度 $\mathcal O(1)$。

可以扩展这个思想来得到其他情况下的算法。


范围大小子集第 \(k\)

给定一个序列,试求其子集 \(S\) 中满足 \(L\le |S|\le R\) 中,权值第 \(k\) 小?

这个时候上一个算法不再适用。因为其过程中 \(|S|\) 会变化且不总是在范围内,无法保证复杂度。

我们考虑进一步构造出一类不改变 \(|S|\) 的偏序结构。先将原序列从小到大排序。则当 \(|S|=t\) 时,最小的方案即为取最小的 \(t\) 个。

考虑一个子集从连续的最小 \(t\) 个转移到当前状态的过程。我们可以先将最大的移到当前的最大位置,然后移次大的,以此类推。

那么可以考虑设状态 \(F(x,y,z)\) 表示目前左端还有连续的 \(x\) 个,除去连续段后最小值在 \(y\) 的位置,次小值值在 \(z\) 的位置。转移考虑:

  • 固定最小值,并拉出一个新的最小值,则最小值成为次小值,即若 \(x\ge 1,x+1<y\),则 \(F(x,y,z)\rightarrow F(x-1,x+1,y)\)
  • 将当前最小值右移,即当 \(y<z\),有 \(F(x,y,z)\rightarrow F(x,y+1,z)\)

不难证明这个转移符合上文的三条性质。

那么我们预先将 \(L\le|S|\le R\) 的每一个 \(|S|\) 对应的最小集合扔进堆里,之后按套路做即可。


扩域第 \(k\)

给定一个序列,其中每个元素是一个可重集,定义一个方案为从每个可重集中选一个数,权值为所选之之和。求权值第 \(k\) 小的方案?

我们首先以每个集合中最小的数作为初始状态。然后按从小到大的顺序做差分。按 \(A_{i,2}\),也即次小值与最小值的差排序。马上你就会知道为什么要这么做。

这个时候我们考虑设状态 \(F(i,j)\) 表示考虑完上述顺序下的前 \(i\) 个可重集,当前可重集选择了第 \(j\) 小的数。那么有转移:

  • 选下一个数,要求 \(j+1\le |A|\)\(F(i,j)+A_{i,j}\rightarrow F(i,j+1)\)
  • 结束当前集合,开始考虑下一个集合。\(F(i,j)+A_{i+1,2}\rightarrow F(i+1,2)\)。(为什么这里是 \(2\)?我们直接跳过了不选的情况吗?实际上不是的:
  • 假如当前集合只选了一个,我们考虑撤回当前集合选择的数并开始考虑下一个集合。也即 \(F(i,2)-A_{i,2}+A_{i+1,2}\rightarrow F(i+1,2)\)

这里第三种转移恰好对应了子集问题中,删去最大值并加入最大值后继的方案。限制只能在 \(j=2\) 时取得是为了保证转移唯一。

现在我们可以回答为什么要按 \(A_{i,2}\) 排序了,这样排序可以保证第三种转移的偏序性。


嵌套第 \(k\)

给定一个序列,其中每个元素是一个可重集,定义一个方案为从每个可重集中选任意多个数,权值为所选数之和。求权值第 \(k\) 小的方案?

可以发现,这里的外层问题是 扩域第 \(k\),内层问题是 子集和第 \(k\)

我们考虑这么一个变换,\(E:S\rightarrow T\)\(T\)\(S\) 中选择任意多个数的所有方案的权值构成的可重集。

假如我们对原序列的所有元素应用变换 \(E\),那么可以套用 扩域第 \(k\) 的方法解决。

但事实上有 \(|T|=2^{|S|}\),因此我们无法直接维护 \(T\),但是我们实际上只会访问 \(T\) 中前 \(\mathcal O(1)\) 项,因此在每个子问题中维护堆支持快速查询第 \(k\) 小,外层可以直接维护。

其实从每个可重集中选任意多个数相当于从他们的并中选任意多个数,但是这不重要,它可以为我们的下一个问题做铺垫。


范围大小子集嵌套第 \(k\) 小 | P6646 [CCO 2020] Shopping Plans

给定一个序列,其中每个元素是一个可重集,定义一个方案为从每个可重集中选 \(t\in[L_i,R_i]\) 个数,权值为所选数之和。求权值第 \(k\) 小的方案?

我们考虑套用上一个问题的算法即可,但是内层问题是 范围大小子集第 \(k\)


参考文献(?

偏序结构第 K 小问题算法小结

关于一类求前 k 优解的问题

posted @ 2026-07-27 09:40  CuteNess  阅读(8)  评论(0)    收藏  举报