「笔记」对顶堆动态维护中位数
写在前面
妈的为啥我不会这个
问题
给定 次操作,要求动态地维护一个可重集合,每次操作为下列三种形式之一:
- 给定参数 ,向集合中插入一个权值 。
- 给定参数 ,删除集合中已存在的一个权值 。
- 查询集合的中位数。
要求在 时间复杂度内实现。
思路
考虑对当前可重集合维护两个 multiset
,记它们分别为 与 , 中存小于等于中位数的权值, 中存大于等于中位数的权值,且钦定 。则在此限制下 中最大的数即为集合的中位数。
考虑在上述限制下如何维护 :
- 为了方便首先在 中插入极小值, 中插入极大值。
- 对于插入操作,若给定参数 ,则将 插入 ,否则插入 。
- 对于删除操作,先查询 中是否存在 ,若存在则直接删除,否则在 中查询并删除。
- 每次插入删除操作后,都进行调整操作:若 则不断取出 中最大值插入 ;若 则不断取出 中最小值插入 。
- 完成调整后,查询 中最大的数即为整个集合的中位数。
发现在上述过程中,单次调整至多仅会调整一个元素,则仅有常数次对 set
的操作。则单次插入/删除时间复杂度 级别,查询 级别。
注意 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。
写在最后
妈的为啥我不会这个
作者@Luckyblock,转载请声明出处。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下