【对顶堆】 黑匣子

传送门

题解

在最开始,黑盒子是空的,并且\(i=0\)
现在对黑盒子进行一系列的操作处理,操作包括以下两种:

  1. \(ADD(x)\):表示将\(x\)加入到黑盒子中。
  2. \(GET\):使\(i\)增加\(1\),输出黑盒子中第\(i\)小的数值(即将所有数按升序排序后的第\(i\)个数)。

定义两个序列

  1. \(A\)表示:这个序列由加入到黑盒子内的所有元素按加入顺序排列后得到
  2. \(U(1),U(2),\dots,U(N)\): 这个序列的第i项表示的是第\(i\)\(GET\)操作时,盒子内元素的数量

根据给出的序列\(A\)\(U\)求出操作过程中输出的所有数值。

数据范围

\(\begin{array}{l}|A(i)|<=2 * 10^{9} \\ 1 \leq N \leq M \leq 30000\end{array}\)
\(p(1 \leq p \leq N), p \leq u(p) \leq M\)

题解

用对顶堆来维护整个有序的序列

  • 一个大根堆存的是当前不会输出的 \(1\sim i-1\) 个元素

  • 一个小根堆存的是当前应输出的位置\(i\sim n\)后的元素

保证大根堆中的元素始终小于小根堆中的元素,因为输出的是当前有序的

  • 大根堆中元素的个数来维护题目中的\(i\)指针

  • 每次输出的时候都想大根堆中多加入一个元素即可

每次插入操作时候后进行检查,若大根堆空的或大根堆顶小于小根堆顶时

  • 将当前元素插入到小根堆中,因为当前的元素可能输出

  • 否则插入到大根堆,然后再将大根堆中的堆顶插入到小根堆中

    • 这样防止了大根堆的堆顶大于小根堆的堆顶

Code

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for(int i=a;i<n;i++)
const int N=3e4+10;
int a[N],u[N];
int n,m;
int main()
{
    cin>>n>>m;
    rep(i,0,n) cin>>a[i];
    rep(i,0,m) cin>>u[i];

    priority_queue<int> mx;
    priority_queue<int,vector<int>,greater<int>> mi;

    int i=0,j=0;

    while(j < m)
    {
        while(i == u[j])
        {
            cout<<mi.top()<<endl;
            mx.push(mi.top());
            mi.pop();
            j++;
        }

        int x=a[i];
        if(mx.empty() || x > mi.top())
            mi.push(x);
        else
        {
            mx.push(x);
            mi.push(mx.top());
            mx.pop();
        }
        i++;
    }
}
posted @ 2020-06-10 11:35  Hyx'  阅读(155)  评论(0)    收藏  举报