题解:学而思编程 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

【核心思想】

  1. 问题分析:需要维护一个动态集合 \(S\),支持三种操作:添加点、删除第 \(n\) 次添加的点、查询与 \(x\) 距离最大的点的距离。这是一个动态最值查询问题,关键在于高效维护集合中的最大值和最小值,并支持删除操作。

  2. 算法选择

    • 对顶堆(大根堆 + 小根堆):使用两个优先队列分别维护集合中的最大值和最小值
    • 大根堆 q1:堆顶为集合中的最大值
    • 小根堆 q2:堆顶为集合中的最小值
    • 懒删除(延迟删除):使用标记数组 f[] 记录元素是否被删除,不立即从堆中移除,而是在查询时清理堆顶已被删除的元素
  3. 关键步骤

    • 初始化:创建大根堆 q1、小根堆 q2,计数器 cnt = 0,标记数组 f[] 初始为 false
    • 添加操作 + x
      • cnt++,为新点分配唯一编号
      • Node{cnt, x} 同时插入 q1q2
    • 查询操作 ? x
      • 清理堆顶:while 循环弹出 q1q2 堆顶已被标记删除的元素
      • 计算最大距离max(q1.top().v - x, x - q2.top().v)
      • 输出结果
    • 删除操作 - n
      • 标记 f[n] = true,表示第 \(n\) 次添加的点已被删除
      • 不立即从堆中移除,延迟到查询时清理
  4. 时间/空间复杂度

    • 时间复杂度:\(O(Q \log Q)\),每次插入和查询操作均为 \(O(\log Q)\),删除操作为 \(O(1)\)
    • 空间复杂度:\(O(Q)\),需要存储所有 \(Q\) 个操作涉及的元素
  5. 对顶堆 + 懒删除的核心思想

    • 对顶堆维护最值:大根堆维护最大值,小根堆维护最小值,实现 \(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
posted @ 2026-06-14 10:45  团爸讲算法  阅读(8)  评论(0)    收藏  举报