#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
const int N = 1e5 + 10;
int h[N], ph[N], hp[N], cnt; // h数组存储堆中的元素,ph数组存储每个元素的父节点索引,
// hp数组存储每个元素在ph数组中的索引,cnt表示堆中元素的数量
// 交换两个堆元素及其在ph数组中的索引
void heap_swap(int a, int b)
{
swap(ph[hp[a]], ph[hp[b]]); // 交换ph数组中的元素
swap(hp[a], hp[b]); // 交换hp数组中的索引
swap(h[a], h[b]); // 交换h数组中的元素
}
// 堆的下沉函数,用于维护最小堆的性质
void down(int u)
{
int t = u; // t用于存储当前节点的索引
// 检查左子节点是否存在,并且左子节点的值是否小于当前节点的值
if (u * 2 <= cnt && h[u * 2] < h[t])
t = u * 2;
// 检查右子节点是否存在,并且右子节点的值是否小于当前节点的值
if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t])
t = u * 2 + 1;
// 如果当前节点不是最小值节点,交换当前节点和最小值节点,并递归下沉
if (u != t)
{
heap_swap(u, t); // 交换当前节点和t节点的值及其索引
down(t); // 对交换后的子树继续进行下沉操作
}
}
// 堆的上浮函数,用于维护最小堆的性质
void up(int u)
{
while (u / 2 && h[u] < h[u / 2]) // 当当前节点小于其父节点时
{
heap_swap(u, u / 2); // 交换当前节点和其父节点的值及其索引
u >>= 1; // 将当前节点设置为其父节点的位置
}
}
int main()
{
int n, m = 0; // n表示操作的总数,m表示插入操作的总数
scanf("%d", &n);
while (n--)
{
char op[5];
int k, x;
scanf("%s", op);
if (!strcmp(op, "I")) // 如果是插入操作
{
scanf("%d", &x); // 读取要插入的元素
cnt++; // 堆中元素数量加1
m++; // 插入操作数量加1
ph[m] = cnt; // 在ph数组中记录当前插入元素的索引
hp[cnt] = m; // 在hp数组中记录当前索引的插入操作编号
h[cnt] = x; // 在h数组中存储插入的元素
up(cnt); // 对新插入的元素进行上浮操作
}
else if (!strcmp(op, "PM")) // 如果是查询最小值操作
printf("%d\n", h[1]); // 输出最小值
else if (!strcmp(op, "DM")) // 如果是删除最小值操作
{
heap_swap(1, cnt); // 将堆顶元素与最后一个元素交换
cnt--; // 堆中元素数量减1
down(1); // 对交换后的堆顶元素进行下沉操作
}
else if (!strcmp(op, "D")) // 如果是删除指定元素操作
{
scanf("%d", &k); // 读取要删除的元素的编号
k = ph[k]; // 获取要删除元素在h数组中的索引
heap_swap(k, cnt); // 将待删除元素与最后一个元素交换
cnt--; // 堆中元素数量减1
up(k); // 对交换后的元素进行上浮操作
down(k); // 对交换后的元素进行下沉操作
}
else // 如果是更新元素值操作
{
scanf("%d%d", &k, &x); // 读取要更新的元素的编号和新值
k = ph[k]; // 获取要更新元素在h数组中的索引
h[k] = x; // 更新元素值
up(k); // 对更新后的元素进行上浮操作
down(k); // 对更新后的元素进行下沉操作
}
}
return 0;
}