124 lines
3.6 KiB
Markdown
124 lines
3.6 KiB
Markdown
|
|
##[$AcWing$ $839$. 模拟堆](https://www.acwing.com/problem/content/841/)
|
|||
|
|
|
|||
|
|
### 一、题目描述
|
|||
|
|
维护一个集合,初始时集合为空,支持如下几种操作:
|
|||
|
|
|
|||
|
|
1. `I x`,插入一个数 $x$;
|
|||
|
|
2. `PM`,输出当前集合中的最小值;
|
|||
|
|
3. `DM`,删除当前集合中的最小值(数据保证此时的最小值唯一);
|
|||
|
|
4. `D k`,删除第 $k$ 个插入的数;
|
|||
|
|
5. `C k x`,修改第 $k$ 个插入的数,将其变为 $x$;
|
|||
|
|
6.
|
|||
|
|
现在要进行 $N$ 次操作,对于所有第 $2$ 个操作,输出当前集合的最小值。
|
|||
|
|
|
|||
|
|
**输入格式**
|
|||
|
|
第一行包含整数 $N$。
|
|||
|
|
|
|||
|
|
接下来 $N$ 行,每行包含一个操作指令,操作指令为 `I x`,`PM`,`DM`,`D k` 或 `C k x` 中的一种。
|
|||
|
|
|
|||
|
|
**输出格式**
|
|||
|
|
对于每个输出指令 `PM`,输出一个结果,表示当前集合中的最小值。
|
|||
|
|
|
|||
|
|
每个结果占一行。
|
|||
|
|
|
|||
|
|
**数据范围**
|
|||
|
|
$1≤N≤10^5$
|
|||
|
|
|
|||
|
|
$−10^9≤x≤10^9$
|
|||
|
|
|
|||
|
|
数据保证合法。
|
|||
|
|
|
|||
|
|
**输入样例:**
|
|||
|
|
```cpp {.line-numbers}
|
|||
|
|
8
|
|||
|
|
I -10
|
|||
|
|
PM
|
|||
|
|
I -10
|
|||
|
|
D 1
|
|||
|
|
C 2 8
|
|||
|
|
I 6
|
|||
|
|
PM
|
|||
|
|
DM
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**输出样例:**
|
|||
|
|
```cpp {.line-numbers}
|
|||
|
|
-10
|
|||
|
|
6
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 二、手写堆的实现
|
|||
|
|
```cpp {.line-numbers}
|
|||
|
|
#include <bits/stdc++.h>
|
|||
|
|
|
|||
|
|
using namespace std;
|
|||
|
|
const int N = 100010;
|
|||
|
|
int h[N], sz; // 堆,堆中元素个数
|
|||
|
|
int p[N]; // 输入序号 与 堆中序号映射关系 p[2]=3 表示第2个输入的,在堆中序号是3号
|
|||
|
|
int q[N]; // 堆中序号 与 输入序号映射关系 q[3]=2 表示堆中序号3号,是第2个输入的
|
|||
|
|
int n, idx; // n:指令数,idx:输入序号
|
|||
|
|
|
|||
|
|
// 交换两个堆中的元素(a,b是指堆中序号)
|
|||
|
|
void heap_swap(int a, int b) {
|
|||
|
|
swap(h[a], h[b]); // 交换堆中序号a与序号b的元素值
|
|||
|
|
swap(q[a], q[b]); // 输入序号交换
|
|||
|
|
swap(p[q[a]], p[q[b]]); // 输入序号与堆中序号的映射关系
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void down(int u) {
|
|||
|
|
int t = u;
|
|||
|
|
// 换到左右儿子和u的最小值
|
|||
|
|
if (u * 2 <= sz && h[u * 2] < h[t]) t = u * 2;
|
|||
|
|
if (u * 2 + 1 <= sz && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
|
|||
|
|
|
|||
|
|
if (u != t) {
|
|||
|
|
heap_swap(u, t);
|
|||
|
|
down(t);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void up(int u) {
|
|||
|
|
while (u / 2 && h[u] < h[u / 2]) { // 爹活着,比爹牛,上位!(玄武门事变?)
|
|||
|
|
heap_swap(u, u / 2);
|
|||
|
|
u /= 2;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
cin >> n;
|
|||
|
|
while (n--) {
|
|||
|
|
string op;
|
|||
|
|
int k, x;
|
|||
|
|
cin >> op;
|
|||
|
|
if (op == "I") {
|
|||
|
|
cin >> x;
|
|||
|
|
p[++idx] = ++sz; // ① 第idx个插入的元素,在堆中的序号是sz
|
|||
|
|
q[sz] = idx; // ② 堆中sz号元素,它是第idx个插入进来的
|
|||
|
|
h[sz] = x; // ③ 堆中sz号元素的值为x
|
|||
|
|
up(sz);
|
|||
|
|
}
|
|||
|
|
if (op == "PM") printf("%d\n", h[1]);
|
|||
|
|
if (op == "DM") {
|
|||
|
|
heap_swap(1, sz); // 用堆中最后一个元素替换掉1号元素
|
|||
|
|
sz--; // 删除尾部元素
|
|||
|
|
down(1); // 重排
|
|||
|
|
}
|
|||
|
|
if (op == "D") {
|
|||
|
|
cin >> k; // 第k个输入序号
|
|||
|
|
k = p[k]; // 获得堆中序号
|
|||
|
|
heap_swap(sz, k); // 将第k个与最后一个交换
|
|||
|
|
sz--; // 删除尾部元素
|
|||
|
|
down(k); // down一下,up一下
|
|||
|
|
up(k);
|
|||
|
|
}
|
|||
|
|
if (op == "C") {
|
|||
|
|
cin >> k >> x; // 第k个输入序号,修改值为x
|
|||
|
|
k = p[k]; // 根据输入序号,查找到堆中序号k
|
|||
|
|
h[k] = x; // 将值修改为x
|
|||
|
|
down(k); // down一下,up一下
|
|||
|
|
up(k);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
```
|