题目来源:AcWing 839. 模拟堆
一、题目描述
维护一个集合,初始时集合为空,支持如下几种操作:
I x
,插入一个数 x x x;PM
,输出当前集合中的最小值;DM
,删除当前集合中的最小值(数据保证此时的最小值唯一);D k
,删除第 k k k 个插入的数;C k x
,修改第 k k k 个插入的数,将其变为 x x x;
现在要进行 N N N 次操作,对于所有第 2 2 2 个操作,输出当前集合的最小值。
输入格式
第一行包含整数
N
N
N。
接下来
N
N
N 行,每行包含一个操作指令,操作指令为 I x
,PM
,DM
,D k
或 C k x
中的一种。
输出格式
对于每个输出指令 PM
,输出一个结果,表示当前集合中的最小值。
每个结果占一行。
数据范围
1
≤
N
≤
1
0
5
1≤N≤10^5
1≤N≤105
−
1
0
9
≤
x
≤
1
0
9
−10^9≤x≤10^9
−109≤x≤109
数据保证合法。
输入样例:
8
I -10
PM
I -10
D 1
C 2 8
I 6
PM
DM
输出样例:
-10
6
二、算法思路
本题的基本操作依然还是来自基础堆,现在需要做的额外的工作是如何维护第 k k k 次插入的数与堆中结点下标 i i i 的映射关系。
首先我们定义两个数组:
h
p
hp
hp[] 和
p
h
ph
ph[],我们需要明白这两个数组的关系和作用:
h
p
hp
hp 是 heap to pointer 的缩写,hp[i] = k
表示堆数组中的下标
i
i
i 所指的结点是第
k
k
k 次插入的。
p
h
ph
ph 是 pointer to heap 的缩写,ph[k] = i
表示第
k
k
k 次插入的元素在堆数组中对应的结点下标
i
i
i。
因此,
h
p
hp
hp 和
p
h
ph
ph 是反函数。
为什么要使用
p
h
ph
ph 和
h
p
hp
hp 数组呢?
原因在于删除第
k
k
k 次插入的元素时,我们必须知道第
k
k
k 次插入的元素在堆数组中的下标
i
i
i,因此使用
p
h
ph
ph 数组可以方便查找。
// 删除第k次插入的元素
if (op == "D")
{
int k;
cin >> k;
k = ph[k]; // 找到第k次插入的元素在堆中的下标
heap_swap(k, n); // 第k次插入的元素和堆尾元素交换
n--; // 删除堆尾元素
up(k), down(k);
}
插入时需要完成的操作:
if (op == "I")
{
int x;
cin >> x;
n++, m++; // n代表堆中元素总数, m代表插入的次数
ph[m] = n, hp[n] = m;
h[n] = x; // 在结尾处插入新元素
up(n);
}
这样看起来,似乎有了
p
h
ph
ph 数组就可以完成所有操作了,但是为什么还要有一个
h
p
hp
hp 数组呢?
原因在于才简单的堆的交换操作中,我们使用的是
s
w
a
p
(
h
[
a
]
,
h
[
b
]
)
swap(h[a], h[b])
swap(h[a],h[b]),传入的参数
a
a
a,
b
b
b 都是堆中的下标,但是无法知道每个堆数组的下标所对应的是第几次插入,因此需要引入
h
p
hp
hp,便于查找.
本质上来说,
h
p
hp
hp 的存在是为了实现交换堆中
a
a
a,
b
b
b 下标元素时,交换
p
h
[
a
]
ph[a]
ph[a] 和
p
h
[
b
]
ph[b]
ph[b]。
// 所有堆内下标的交换都换成这个
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]);
swap(hp[a], hp[b]);
swap(h[a], h[b]);
}
三、代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N], hp[N], ph[N];
int n, m;
// 堆内交换操作传入的是堆中的下标
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]); // 这个才是核心
swap(hp[a], hp[b]); // 这个是辅助
swap(h[a], h[b]);
}
void down(int u)
{
int t = u;
if (u * 2 <= n && h[t] > h[u * 2]) t = u * 2;
if (u * 2 + 1 <= n && h[t] > h[u * 2 + 1]) t = u * 2 + 1;
if (t != u)
{
heap_swap(t, u);
down(t);
}
}
void up(int u)
{
while (u / 2 && h[u] < h[u / 2])
{
heap_swap(u, u / 2);
u >>= 1;
}
}
int main()
{
int s;
cin >> s;
string op;
while (s--)
{
cin >> op;
if (op == "I")
{
int x;
cin >> x;
n++, m++;
hp[n] = m, ph[m] = n;
h[n] = x;
up(n);
}
else if (op == "PM") cout << h[1] << endl;
else if (op == "DM")
{
heap_swap(1, n);
n--;
down(1);
}
else if (op == "D")
{
int k;
cin >> k;
k = ph[k];
heap_swap(k, n);
n--;
down(k), up(k);
}
else
{
int k, x;
cin >> k >> x;
k = ph[k];
h[k] = x;
down(k), up(k);
}
}
return 0;
}