普通的堆只能要么维护最小值要么维护最大值,对于本题这种需要同时维护最小最大值的数据结构,我们可以先直接各开一个小根堆大根堆,剩下的重点则是保证每个插入数据的唯一性。

对此,一种非常经典的保证数据唯一性的方法,我称其为「惰性删除」。具体而言,我们首先给每个插入数据分配唯一的序列号 serial,然后新建一个布尔数组 ondelete,其中 ondelete[x] 表示序列号为 x 的数据是否已经被删除。

建立堆的时候,插入的数据类型为 pair<int, int>,第一个是数据值 ,第二个是序列号 serial。根据 C++ 里面 pair 的默认比较方式, 仍然会先被比较,所以直接使用不会有影响。对于堆顶元素,我们可以通过其序列号查询它有没有被删除。如果根据 ondelete 数组它已经被删了,则直接弹出,直到堆为空或发现一个没被删的元素。

参考代码:

#include <cstdlib>
#include <iostream>
#include <functional>
#include <queue>
#include <utility>
#include <vector>
using namespace std;

int main() {

    int n;
    cin >> n;

    using node = pair<int, int>;
    priority_queue<node, vector<node>, greater<>> minpq;
    priority_queue<node> maxpq;
    vector<char> ondelete;
    ondelete.reserve(n);
    int serial = 0;

    for(int i = 0; i < n; ++i) {
        int op;
        cin >> op;
        switch (op) {
        case 1: {
            int x;
            cin >> x;
            minpq.emplace(x, serial);
            maxpq.emplace(x, serial);
            ondelete.emplace_back(0);
            ++serial;
            break;
        }
        case 2: {
            for(; !minpq.empty() && ondelete[minpq.top().second]; minpq.pop());
            cout << minpq.top().first << '\n';
            break;
        }
        case 3: {
            for(; !maxpq.empty() && ondelete[maxpq.top().second]; maxpq.pop());
            cout << maxpq.top().first << '\n';
            break;
        }
        case 4: {
            for(; !minpq.empty() && ondelete[minpq.top().second]; minpq.pop());
            ondelete[minpq.top().second] = 1;
            minpq.pop();
            break;
        }
        case 5: {
            for(; !maxpq.empty() && ondelete[maxpq.top().second]; maxpq.pop());
            ondelete[maxpq.top().second] = 1;
            maxpq.pop();
            break;
        }
        }
    }
}