题解:学而思编程 x轴最大距离
【题目来源】
学而思编程:x轴最大距离
【题目描述】
考虑x轴上的点组成的集合 \(S\),你需要写一个数据结构,支持以下操作:
(1)+ x:添加一个坐标为 \(x\) 的点到集合 \(S\) 中。
(2)- n:删除第 \(n\) 次添加的点,添加操作按顺序从 \(1\) 编号,其他操作不参与计数。
(3)? x:计算 \(S\) 集合中,与 \(x\) 的距离最大的点的距离,保证询问时,集合 \(S\) 中,至少有一个点。
两个点 \(x_1\) 和 \(x_2\) 的距离等于坐标之差的绝对值:\(|x_1−x_2|\)。
【输入】
第 \(1\) 行,\(1\) 个正整数 \(Q\),表示操作的总数。
接下来 \(Q\) 行,每行包含一条操作。
【输出】
对每个询问操作,输出最大的距离。
【输入样例】
5
+ 8
+ -1
? 5
- 2
? -100
【输出样例】
6
108
【核心思想】
-
问题分析:需要维护一个动态集合 \(S\),支持三种操作:添加点、删除第 \(n\) 次添加的点、查询与 \(x\) 距离最大的点的距离。这是一个动态最值查询问题,关键在于高效维护集合中的最大值和最小值,并支持删除操作。
-
算法选择:
- 对顶堆(大根堆 + 小根堆):使用两个优先队列分别维护集合中的最大值和最小值
- 大根堆
q1:堆顶为集合中的最大值 - 小根堆
q2:堆顶为集合中的最小值 - 懒删除(延迟删除):使用标记数组
f[]记录元素是否被删除,不立即从堆中移除,而是在查询时清理堆顶已被删除的元素
-
关键步骤:
- 初始化:创建大根堆
q1、小根堆q2,计数器cnt = 0,标记数组f[]初始为false - 添加操作
+ x:cnt++,为新点分配唯一编号- 将
Node{cnt, x}同时插入q1和q2
- 查询操作
? x:- 清理堆顶:while 循环弹出
q1和q2堆顶已被标记删除的元素 - 计算最大距离:
max(q1.top().v - x, x - q2.top().v) - 输出结果
- 清理堆顶:while 循环弹出
- 删除操作
- n:- 标记
f[n] = true,表示第 \(n\) 次添加的点已被删除 - 不立即从堆中移除,延迟到查询时清理
- 标记
- 初始化:创建大根堆
-
时间/空间复杂度:
- 时间复杂度:\(O(Q \log Q)\),每次插入和查询操作均为 \(O(\log Q)\),删除操作为 \(O(1)\)
- 空间复杂度:\(O(Q)\),需要存储所有 \(Q\) 个操作涉及的元素
-
对顶堆 + 懒删除的核心思想:
- 对顶堆维护最值:大根堆维护最大值,小根堆维护最小值,实现 \(O(1)\) 获取集合最值
- 懒删除策略:通过标记数组延迟删除,避免在堆中直接查找删除元素的 \(O(n)\) 复杂度
- 查询时清理:每次查询前先清理堆顶已被删除的元素,保证结果正确性
- 双向维护:同一元素同时存在于两个堆中,删除时只需标记,无需同步删除
- 适用于需要支持删除操作的动态最值查询场景
【算法标签】
堆排序
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct Node
{
int id, v; // id: 元素编号, v: 元素值
};
priority_queue<Node> q1; // 大根堆
priority_queue<Node, vector<Node>, greater<Node> > q2; // 小根堆
// 定义大根堆的比较操作
bool operator<(Node x, Node y)
{
return x.v < y.v;
}
// 定义小根堆的比较操作
bool operator>(Node x, Node y)
{
return x.v > y.v;
}
int q, cnt; // cnt: 元素计数器
bool f[N]; // 标记元素是否被删除
int main()
{
cin >> q;
while (q--)
{
char op;
int x;
cin >> op >> x;
if (op == '+')
{
cnt++;
q1.push(Node{cnt, x}); // 插入大根堆
q2.push(Node{cnt, x}); // 插入小根堆
}
else if (op == '?')
{
// 清理堆顶已被删除的元素
while (f[q1.top().id] == true)
{
q1.pop();
}
while (f[q2.top().id] == true)
{
q2.pop();
}
// 计算最大值
int maxn = max(q1.top().v - x, x - q2.top().v);
cout << maxn << endl;
}
else
{
f[x] = true; // 标记删除
}
}
return 0;
}
【运行结果】
5
+ 8
+ -1
? 5
6
- 2
? -100
108
浙公网安备 33010602011771号