堆 trick 二则

垃圾堆

\(\gdef\top{\operatorname{top}}\)

用法与实现

垃圾堆是实现堆删除任意元素的一种实现方式,具体的,如果要删除堆 \(h\) 内一个元素,我们先不操作 \(h\),而是新开一个堆 \(h'\),把这个元素 push\(h'\) 内,那么此时显然有 \(h'\subseteq h\)。这样,查询 \(h\) 最值时,我们可以两边同时 pop,因为 \(h'\) 里面所有元素都在 \(h\) 内,故如果 \(h\) 原堆顶本应被删除,此时 \(\top(h)=\top(h')\)。一直删掉,直到 \(\top(h)\ne\top(h')\) 即可,此时 \(\top(h)\notin h'\),所以这个元素没被删。

时间复杂度

考虑最坏情况,每个元素都被删,注意到每个元素都会:

  1. 进一次 \(h\)
  2. 进一次 \(h'\)
  3. 同时从 \(h\)\(h'\)pop 掉,

故这东西的时间复杂度是优越的 \(O(n\log_2n)\),其中 \(n\) 是堆内不同元素的个数。

这东西似乎比对顶堆还冷门,而且似乎可以用 multiset 代替,但考虑到 multiset 的大常数。

对顶堆

用法与实现

对顶堆支持维护集合中位数(好像固定第 \(k\) 大也能维护),具体的,我们开两个堆 \(h_1,h_2\)\(h_1\) 是大根堆,存放小于中位数的数;\(h_2\) 是小根堆,存放不小于中位数的数,每次访问中位数就取 \(h_2\) 的堆顶即可。当然访问 \(h_1\) 以获得中位数也不是不行,稍微调整一下即可。

往集合里面插入一个数 \(x\),先看堆顶,若 \(\top(h_1)>x\),那么 \(x\) 应该取 \(h_1\),否则去 \(h_2\),插入后要维持平衡,看两个堆大小是否相同(奇数个元素的话看 \(h_2\) 的大小是否刚好比 \(h_1\) 的大小大 \(1\))。

时间复杂度

显然有单次插入均摊 \(O(\log_2n)\),查询 \(O(1)\)

posted @ 2026-08-10 10:31  Sternenlicht12345  阅读(17)  评论(0)    收藏  举报