首页 > 其他分享 >从2023济南K学习滑动窗口中位数问题

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

时间:2024-10-03 16:49:49浏览次数:9  
标签:qErase big top 中位数 pop 2023 qPush 滑动 size

板子

  • 对顶堆
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-qErase
    i64 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-qErase
    i64 sum;//维护出这个堆对应的sum
 
    Heap() : sum{0} {}
 
    void push(T x) {//加入的同时更新sum
        sum += x;
        qPush.push(x);
    }
 
    void erase(T x) {//删除的同时更新sum
        sum -= x;
        qErase.push(x);
    }
 
    T top() {
        while (!qErase.empty() && qPush.top() == qErase.top())
            qPush.pop(), qErase.pop();
        return qPush.top();
    }
 
    void pop() {//一样,更新sum
        while (!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 root
    Heap<T, std::less<T>> big;      // big root
 
    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();
    }

    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 tests
    int 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';

}

标签:qErase,big,top,中位数,pop,2023,qPush,滑动,size
From: https://www.cnblogs.com/kdlyh/p/18445811

相关文章

  • The 2023 ICPC Asia Macau Regional Contest
    A.(-1,1)-Sumplete首先只取\(-1\),这样的话选1和不选-1产生的贡献就是都是+1。枚举每一行,然后贪心选择需求最大的若干列。#include<bits/stdc++.h>usingnamespacestd;usingi32=int32_t;usingi64=longlong;#defineinti64usingvi=vector<int>;usingpi......
  • 2023-9-30
    标签之文本标签列表标签之有序列表列表标签之无序列表......
  • The 2023 ICPC Asia Nanjing Regional Contest / The 2nd Universal Cup. Stage 11: N
    比赛链接I.Counter按时间排序即可,注意可以不清零。F.EquivalentRewriting对于每个位置,把所有有这个位置的操作编号连向这个位置最终的值,做个拓扑排序,看看字典序最大的即可。复杂度\(\Theta(n+m)\)。C.PrimitiveRoot枚举和\(m\)的公共前缀,设\(i\)位置\(m\)是\(1......
  • idea2023-快速搭建一个本地tomcat的javaWeb项目(从0到1保姆教学)
    前言如何在新版idea中搭建一个javaWeb项目,并且应用在物理的tomcat中,本文将进行从零到一,完成搭建步骤,以及相关注意事项的讲解。为什么需要配置tomcat?我们开发的javaWeb项目,最后都需要打包部署到真正的物理tomcat上发布运行;在开发阶段,我们想要测试javaWeb项目,除了使用maven......
  • 滑动窗口->dd爱框框
    1.题目:  2.题解:2.1为什么用滑动窗口优化:因为元素都是大于0的所以:当找到大于等于x的值时,right可以不用返回两个指针都往后走;因此可以使用滑动窗口优化暴力解法  2.2:滑动窗口具体使用步骤:1.进窗口:sum+=array[right];2.判断:sum>=x时出窗口    灵活......
  • 2023护网行动经验分享(2024护网招人)
    今年的护网又开始摇人了,不知道大家有想法没?去年的护网结束之后,朋友圈感觉是在过年,到处是倒计时和庆祝声。看得出来防守方们7*24小时的看监控还是比较无奈的。本次复盘基于我对整个护网行动的观察总结而来,仅代表我个人观点,如有不妥之处,欢迎交流。1.整体攻防的思考本次......
  • [2023四校联考3]sakuya 题解(根号分治)
    题目链接。题目分析第一个操作类似哈希冲突那一道题,可以运用类似的思路开一个二维表,很容易想到两种做法:开一个二维表,表上的第\(i\)行,第\(j\)列表示序列下标在模\(i\)意义下等于\(j\)的加法标记。对于修改操作,直接暴力修改对应的那一行的值即可,查询时用线段树查询那个......
  • The 2023 ICPC Asia Jinan Regional Contest (The 2nd Universal Cup. Stage 17: Jina
    赛时4题,策略重大失误,g题思路假了但是以为是代码问题硬调3.5h,m题本来是可以过的,e是网络流说不定也能过呢。xixike大力平衡树直接打过k题省去思考双优先队列算法的时间,太强A观察到同级同形状括号如果有四个就一定可以交换顺序,而且是充要的,经典括号匹配用栈存储就过了,我代码比较丑......
  • 2023年“物天长跑月”策划案
    主办方:物理与天文学院承办方:物理与天文学院学生会活动时间:2023年11月4日-2023年12月4日2023年10月18日望晟 目录一、活动方案1、活动目的2、活动主题3、活动时间4、活动对象5、活动详细内容5.1活动形式5.2打卡规则5.3活动场地5.4积分规则5.5活动奖项二、活动物......
  • 极客大挑战2023-pwn-nc_pwntools WriteUp
    主要考查点Pwntools工具的基本使用方法解题思路1.nc连接题目,得到提示:根据题目,要求发送一个100长度的字符串,而且末尾需要为Sycloverb'A'*92+b'Syclover'2.发送第一个请求后进入第二步要求短时间内计算一个复杂算式,自己算是肯定不可能的,所以pwntools的recv来接收并完成......