你需要维护一个在数轴上的线段的集合 \(S\),支持两种操作:
A l r
表示将 \(S\) 中所有与线段 \([l,r]\) 相交的线段删去,并将 \([l,r]\) 加入 \(S\) 中。B
查询 \(S\) 中的元素数量。
对于 A
操作,每次还需输出删掉的元素个数。
这道题可以用 std::set + 重载运算符的技巧过掉,具体的说,我们将线段写成结构体,并重载小于号:
struct Seg {int l, r;bool operator<(const Seg& o) const {return r < o.l;}
};
考虑当我们插入一条线段 \(seg\) 时,set 会怎样工作。左端点在 \(seg\) 的右端点右边的线段,会排在 \(seg\) 的右边;右端点在 \(seg\) 左端点左边的线段,会排在 \(seg\) 的左边。它们与 \(seg\) 不相交。
而当我们查找 \(seg\),“最小的”线段是 set 中第一个和 \(seg\) 相交的线段(在这个规则下,相交就类似相等)。所以对于 A
操作,我们从这个线段出发,一个一个删掉和 \(seg\) 相交的线段即可。因为最多有 \(n\) 次操作,删除也是 \(O(n\log n)\) 的。
这个 trick 相当于说,重载小于号不一定要做一个偏序关系,可以跳出固定思维,让它表示一些特殊的关系,方便解决实际问题。
参考:https://www.luogu.com.cn/article/sprx0wtn
下面是 AC 代码:
#include <bits/stdc++.h>
using namespace std;struct Seg {int l, r;bool operator<(const Seg& o) const {return r < o.l;}
};int main() {cin.tie(0)->sync_with_stdio(0);cout.tie(0);int n;cin >> n;set<Seg> s;for (int i = 0; i < n; i++) {string op;cin >> op;if (op == "A") {int l, r, cnt = 0;cin >> l >> r;Seg seg(l, r);auto it = s.find(seg);while (it != s.end()) {s.erase(it);it = s.find(seg);cnt++;}cout << cnt << '\n';s.insert(seg);} else {cout << s.size() << '\n';}}return 0;
}