从2023济南K学习滑动窗口中位数问题

news/2024/10/3 17:02:52

板子

  • 对顶堆
template<class T>
struct DualHeap {Heap<T, std::greater<T>> small; // 小根堆,里面存放大的值Heap<T, std::less<T>> big;      // 大根堆,里面存放前k小的值//中位数就是big.top()DualHeap() {}void update() {if (big.size() == 0 and small.size() == 0) {return;}while (big.size() > small.size() + 1) {T x = big.top();big.pop();small.push(x);}while (big.size() < small.size()) {T x = small.top();small.pop();big.push(x);}}void push(T val) {if (big.size() == 0) {big.push(val);return;}if (val <= big.top()) {big.push(val);} else {small.push(val);}update();}void erase(T val) {assert(big.size() >= 1);if (val <= big.top()) {big.erase(val);} else {small.erase(val);}update();}};
  • 可删堆
template <class T, class Cmp = std::less<T>>
struct Heap {//可删堆std::priority_queue<T, std::vector<T>, Cmp> qPush, qErase; // Heap=qPush-qErasei64 sum;Heap() : sum{0} {}void push(T x) {qPush.push(x);}void erase(T x) {qErase.push(x);}T top() {while (!qErase.empty() && qPush.top() == qErase.top())qPush.pop(), qErase.pop();return qPush.top();}void pop() {while (!qErase.empty() && qPush.top() == qErase.top()) {qPush.pop(), qErase.pop();}qPush.pop();}int size() {return qPush.size() - qErase.size();}
};

滑动窗口中位数

https://codeforces.com/gym/104901/problem/K

选区间内一个数,让区间内每个数到这个数的距离之和最小,动态维护这个距离之和

首先一个经典结论,这个数就是中位数。

那么问题转化成:滑动窗口维护区间每个数到中位数的距离之和 \(ans\)

显然,ans 是所有比中位数 \(mid\) 与每个比中位数小的数 \(less\) 的差加上每个比中位数大的数 \(large\) 与中位数的差,也就是

\[ans = cnt_{less}\times mid - sum_{less} + sum_{large} - cnt_{large}\times mid \]

那么我们就是要维护三个信息:

\(less, large, mid\)

这很像对顶堆,less和large的相关信息都完全可以对应上其中的大根堆和小根堆

单纯的对顶堆,是不支持删除的。

想要维护滑动窗口的中位数,就得结合可删堆。

直接从应用入手,把两个对顶堆改成可删堆其中即可,而对 sum 的动态维护则在可删堆的 pushpoperase 操作中实现即可,非常直观。

template <class T, class Cmp = std::less<T>>
struct Heap {//可删堆std::priority_queue<T, std::vector<T>, Cmp> qPush, qErase; // Heap=qPush-qErasei64 sum;//维护出这个堆对应的sumHeap() : sum{0} {}void push(T x) {//加入的同时更新sumsum += x;qPush.push(x);}void erase(T x) {//删除的同时更新sumsum -= x;qErase.push(x);}T top() {while (!qErase.empty() && qPush.top() == qErase.top())qPush.pop(), qErase.pop();return qPush.top();}void pop() {//一样,更新sumwhile (!qErase.empty() && qPush.top() == qErase.top()) {qPush.pop(), qErase.pop();}sum -= qPush.top();qPush.pop();}int size() {//真实的个数也可以通过可删堆维护出来return qPush.size() - qErase.size();}
};template<class T>
struct DualHeap {//结合可删堆,形成可删对顶堆Heap<T, std::greater<T>> small; // small rootHeap<T, std::less<T>> big;      // big rootDualHeap() {}void update() {if (big.size() == 0 and small.size() == 0) {return;}while (big.size() > small.size() + 1) {T x = big.top();big.pop();small.push(x);}while (big.size() < small.size()) {T x = small.top();small.pop();big.push(x);}}void push(T val) {if (big.size() == 0) {big.push(val);return;}if (val <= big.top()) {big.push(val);} else {small.push(val);}update();}void erase(T val) {assert(big.size() >= 1);if (val <= big.top()) {big.erase(val);} else {small.erase(val);}update();}i64 getResult() {if (big.size() == 0) {return 0;}//说明只有一个数int x{big.top()}; i64 ans1 = 1LL * x * big.size() - big.sum;//比中位数小的数的贡献i64 ans2 = small.sum - 1LL * x * small.size();//比中位数大的数的贡献return ans1 + ans2;}};void solve()
{
#define testsint n; i64 k; std::cin >> n >> k; std::vector<int> a(n); for (auto& ai : a) {std::cin >> ai; --ai;} for (int i = 0; i < n; i++) {a[i] -= i;}DualHeap<int> dheap; int ans{}; for (int l = 0, r = 0; r < n; r++) {//这题固定右指针,移动左指针更方便dheap.push(a[r]);while (l <= r and dheap.getResult() > k) {//如果不满足条件,就继续移动左指针dheap.erase(a[l]);l += 1;}ans = std::max(ans, r - l + 1);}std::cout << ans << '\n';}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/67406.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

【闲话】高一上运动会

“加油,加油!”虽然没有上场,但记忆也为本次运动会的举办做出了许多努力!想喝矿泉水的话,就请记忆帮你拿一瓶吧!心跳节拍弥梦离 “加油,加油!”虽然没有上场,但记忆也为本次运动会的举办做出了许多努力!想喝矿泉水的话,就请记忆帮你拿一瓶吧!活力四射超神龙女 代表…

信息学奥赛复赛复习10-CSP-J2020-03表达式求值-栈、后缀表达式、isdigit函数、c_str函数、atoi函数、链式前向星、数据结构、深度优先搜索

PDF文档公众号回复关键字:202410031 P7073 [CSP-J2020] 表达式 [题目描述] 小 C 热衷于学习数理逻辑。有一天,他发现了一种特别的逻辑表达式。在这种逻辑表达式中,所有操作数都是变量,且它们的取值只能为 0 或 1,运算从左往右进行。如果表达式中有括号,则先计算括号内的子…

群晖docker实现稍后阅读wallabag

开篇 本文基于 docker 和 github 开源项目 wallabag 关于群晖安装, 在项目的说明文档里面显示他们在群晖社区里面提供了一个套件, 但我在添加社区以后并没有找到, 所以采用了 docker 方式 拉取镜像Ssh 链接群晖, sudo -i 进入 root 权限 使用命令 docker run -v /opt/wallabag/…

bing chat 该服务在你所在的地区不可用。

一是:在浏览器搜索结果页-更多中,将国家或地区更改为非中国大陆。 二是:在浏览器设置-语言中,将语言更改为非中文简体的语言,这里语言是可以更改回来。 三是:重新注册一个新的Microsoft 账号,推荐谷歌邮箱进行注册。编程是个人爱好

[错误代码]SQLSTATE[HY000] [1045] Access denied for user 20241001@localhost (using password: YES)

这个错误提示 SQLSTATE[HY000] [1045] Access denied for user 20241001@localhost (using password: YES) 表示 MySQL 认证失败,通常是由于用户名或密码不正确导致的。 解决方法检查用户名和密码 确认使用的用户名和密码是否正确。重置密码 如果忘记密码,可以重置密码。检查…

国庆快乐!附ssh实战

小伙伴们,有一段时间没更新了,目前在中科院软件所实习,在这里我祝大家国庆快乐!今天这一期带来ssh命令的实战教程,ssh在工作当中遇到的非常多,因为总是需要登服务器,而且玩法也有不少,这是我常用的几个玩法。 1、Windows直接连接虚拟机启动的Linux。 ssh user@IPV42、从…

当前页面出现致命错误,详细报错请切换至开发模式调试

切换到开发模式:获取详细的错误信息。 检查列定义:确认 Color 列的定义是否合理。 修改列定义:如果需要,增加列的长度。 重新导入数据:确保数据符合新的列定义。希望这些步骤能帮助你解决问题。如果有更多详细的信息,请随时提供。扫码添加技术【解决问题】专注中小企业网…

【软考】2 码制 / 机器数

概念 机器数只能以二进制方式表示,大类分为【无符号数】和【有符号数】 【无符号数】在机器数中没有符号,表示正数 【有符号数】在机器数中有符号,包含正数的其他数值,存在四种操作:【原码】【反码】【补码】【移码】 一、原码 最高位作为符号位进行正数和负数表示 剩余低…