「笔记」对顶堆动态维护中位数

写在前面

妈的为啥我不会这个

问题

给定 \(n\) 次操作,要求动态地维护一个可重集合,每次操作为下列三种形式之一:

  • 给定参数 \(x\),向集合中插入一个权值 \(x\)
  • 给定参数 \(x\),删除集合中已存在的一个权值 \(x\)
  • 查询集合的中位数。

要求在 \(O(n\log n)\) 时间复杂度内实现。

思路

考虑对当前可重集合维护两个 multiset,记它们分别为 \(A\)\(B\)\(A\) 中存小于等于中位数的权值,\(B\) 中存大于等于中位数的权值,且钦定 \(\operatorname{size}(A) \ge \operatorname{size}(B), \left|\operatorname{size}(A) - \operatorname{size}(B)\right|\le 1\)。则在此限制下 \(A\) 中最大的数即为集合的中位数。

考虑在上述限制下如何维护 \(A, B\)

  • 为了方便首先在 \(A\) 中插入极小值,\(B\) 中插入极大值。
  • 对于插入操作,若给定参数 \(x\le \operatorname{max}\{A\}\),则将 \(x\) 插入 \(A\),否则插入 \(B\)
  • 对于删除操作,先查询 \(A\) 中是否存在 \(x\),若存在则直接删除,否则在 \(B\) 中查询并删除。
  • 每次插入删除操作后,都进行调整操作:若 \(\operatorname{size}(A) > \operatorname{size}(B) + 1\) 则不断取出 \(A\) 中最大值插入 \(B\);若 \(\operatorname{size}(B) > \operatorname{size}(A)\) 则不断取出 \(B\) 中最小值插入 \(A\)
  • 完成调整后,查询 \(A\) 中最大的数即为整个集合的中位数。

发现在上述过程中,单次调整至多仅会调整一个元素,则仅有常数次对 set 的操作。则单次插入/删除时间复杂度 \(O(\log n)\) 级别,查询 \(O(1)\) 级别。

注意 multiset.erase 时,若传入的为值则会将所有值均删除;传入迭代器才仅会删除一个。

代码

namespace Set {
  const int kInf = 1e9 + 2077;
  std::multiset<int> less, greater;
  void init() {
    less.clear(), greater.clear();
    less.insert(-kInf), greater.insert(kInf);
  }
  void adjust() {
    while (less.size() > greater.size() + 1) {
      std::multiset<int>::iterator it = (--less.end());
      greater.insert(*it);
      less.erase(it);
    }
    while (greater.size() > less.size()) {
      std::multiset<int>::iterator it = greater.begin();
      less.insert(*it);
      greater.erase(it);
    }
  }
  void add(int val_) {
    if (val_ <= *greater.begin()) less.insert(val_);
    else greater.insert(val_);
    adjust();
  }
  void del(int val_) {
    std::multiset<int>::iterator it = less.lower_bound(val_);
    if (it != less.end()) {
      less.erase(it);
    } else {
      it = greater.lower_bound(val_);
      greater.erase(it);
    }
    adjust();
  }
  int get_middle() {
    return *less.rbegin();
  }
}

例题

The 2023 ICPC Asia Jinan Regional Contest - K

写在最后

妈的为啥我不会这个

posted @ 2024-04-26 10:44  Luckyblock  阅读(196)  评论(0编辑  收藏  举报