题解:洛谷 AT_abc451_c Understory

【题目来源】

洛谷:AT_abc451_c [ABC451C] Understory - 洛谷

【题目描述】

高桥正在管理他花园里的树的数量。最开始,花园里没有任何树。

给出 \(Q\) 个操作,每个操作有以下两种类型之一。在处理完每个操作后,立即输出当前花园中树的数量。

  • 1 h :在花园中种下一棵高度为 \(h\) 的新树。
  • 2 h :将花园中所有高度不超过 \(h\) 的树全部移除。

【输入】

输入通过标准输入给出,格式如下:

\(Q\)
\(\text{query}_1\)
\(\text{query}_2\)
⋮
\(\text{query}_Q\)

第 \(i\) 个操作 \(\text{query}_i\) 为以下两种之一:

\(1\) \(h\)
\(2\) \(h\)

【输出】

输出 \(Q\) 行。

第 \(i\) 行输出在处理完第 \(i\) 个操作后,当前花园中树的数量。

【输入样例】

5
1 5
1 7
1 8
2 7
1 3

【输出样例】

1
2
3
1
2

【核心思想】

  1. 问题分析:维护一个动态树高集合,支持两种操作:插入高度 \(h\) 的树,删除所有高度 \(\leq h\) 的树。每次操作后输出当前树木数量。这是一个动态集合维护 + 区间删除问题,核心在于高效支持"删除所有 \(\leq h\) 的元素"这一操作。

  2. 算法选择:

    • 多重集合(Multiset)+ 区间删除:利用 multiset 的有序性和 erase(begin, it) 的区间删除功能,在 \(O(\log n + k)\) 时间内删除所有高度 \(\leq h\) 的树(\(k\) 为删除数量)
  3. 关键步骤:

    • 读入数据:\(Q\),以及 \(Q\) 个查询
    • 操作处理:
      • 1 h:trees.insert(h),插入高度 \(h\)
      • 2 h:it = trees.upper_bound(h),找到第一个高度 \(> h\) 的位置;trees.erase(trees.begin(), it),删除 \([\text{begin}, it)\) 内所有元素(即高度 \(\leq h\) 的所有树)
    • 输出:每次操作后 trees.size()
  4. 时间/空间复杂度:

    • 时间复杂度:\(O(Q \log N)\),每次插入 \(O(\log N)\),每次删除 \(O(\log N + k)\)(\(k\) 为删除数量,均摊分析下总删除次数不超过总插入次数)
    • 空间复杂度:\(O(N)\),存储当前所有树的高度
  5. 多重集合与区间删除的核心思想:

    • 有序性利用:multiset 内部按高度有序排列,upper_bound(h) 返回第一个 \(> h\) 的迭代器,其左侧所有元素即为高度 \(\leq h\) 的树
    • 区间删除的高效性:erase(begin, it) 一次性删除区间 \([\text{begin}, it)\) 内的所有元素,时间复杂度与删除元素数量线性相关,避免了逐个删除的 \(O(k \log n)\) 开销
    • 动态计数:size() 在 \(O(1)\) 时间内返回当前集合大小,满足每次操作后立即输出的要求
    • 多重集合的必要性:允许相同高度的树存在(不同树高度可以相同),multiset 支持重复元素,而 set 不支持
    • 适用于动态有序集合维护、区间批量删除类问题

【算法标签】

普及- #集合

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int q;  // 查询次数
multiset<int> trees;  // 多重集合存储树的高度

int main()
{
    cin >> q;  // 输入查询次数
    while (q--)  // 处理每个查询
    {
        int op, h;
        cin >> op >> h;  // 输入操作类型和高度

        if (op == 1)  // 操作1:种树
        {
            trees.insert(h);  // 插入一棵高度为h的树
        }
        else  // 操作2:砍树
        {
            auto it = trees.upper_bound(h);  // 找到第一个高度>h的树
            trees.erase(trees.begin(), it);  // 删除所有高度≤h的树
        }
        cout << trees.size() << endl;  // 输出剩余树的数量
    }
    return 0;
}

【运行结果】

5
1 5
1
1 7
2
1 8
3
2 7
1
1 3
2
posted @ 2026-08-26 10:12  团爸讲算法  阅读(2)  评论(0)    收藏  举报