Wonderful Impostors
注意:全文左闭右开。
Hint 0: CF 的数据结构题难度自动 -500。
Hint 1: 若干个需求可能被同时满足的充分必要条件是什么?对于单次询问,如何暴力回答?
将所有没有内鬼的区间并起来,记作 ,那么 中的任意一个人都是船员。倘若存在一个有内鬼的区间是 的子集,便出现了冲突。
我们可以开一颗值域为 的线段树,初始为 ,将所有没有内鬼的区间在线段树上执行区间 操作。然后对于所有有内鬼的区间挨个检查,若这段区间的最小值不是 ,则说明这段区间被完全包含掉了。
Hint 2:在线处理询问好像很难做。有什么离线处理的方法吗?
每个区间的回答都是 YES 或 NO,并且一段区间 的子区间的回答一定不会劣于 。由此我们联想到 [NOI2024] 集合 中的双指针离线。具体来说,固定左边界(记为 ),一直向右移动右边界直到出现冲突(记此时的右边界为 ),此时对于左边界为 的所有询问,其右边界小于 的都合法,大于等于 的都不合法。
Hint 3:当新增一条区间内有内鬼的条件,该如何检查加入该条件后是否会产生冲突?请注意你的实现应该易于从中删除一组询问。
跟暴力做法类似,开一颗值域为 的线段树,初始为 。每加入一个区间内没有内鬼的条件,就在线段树上对应范围执行区间 操作。想要检查添加区间存在内鬼的条件后会不会出现冲突,只需要判断对应区间的最小值是不是 就好了。删除也很简单,执行区间 操作即可。
Hint 4:当新增一条区间内没有内鬼的条件,该如何检查加入该条件后是否会产生冲突?
最为复杂的情况如图。黑色表示不存在内鬼的约束,红色表示存在内鬼的约束。
若干个区间并起来的结果为若干个互不相交的大区间。添加一个新的区间只会对其中一小部分有影响,其他的不变。有影响的大区间范围可以通过在上面所说的线段树上二分取得。具体来讲,通过线段树二分找到第一个为 的数。
找到一个大区间 后,还需要判断这个大区间有没有完全包含一个存在内鬼的区间。这相当于要处理一个二维偏序问题:是否存在二元组 满足 且 ?再开一颗单点修改、区间 min 线段树,将第一关键字 作为下标,线段树上存放所有第一关键字为 的二元组中最小的 。在新增 / 删除区间有内鬼的约束时,同步维护一个数组套 multiset,下标为 的 multiset 存放所有第一关键字为 的二元组所对应的 ,在线段树上同步取其最小值。查询时,查询 的区间最小值是否小于 即可。
// https://github.com/Mr-Python-in-China/mp-oi-library/blob/main/library/
#include <mrpython/typical_segment_tree.hpp>
#include <mrpython/lazy_segment_tree.hpp>
#include <iostream>
#include <set>
using namespace std;
istream& fin = cin;
ostream& fout = cout;
using ui = unsigned int;
using uli = unsigned long long int;
using li = long long int;
struct Question {
bool c;
size_t l, r;
};
struct AddMinNode {
using T = ui;
using Lazy = ui;
static T merge_data(T l, T r) { return min(l, r); }
static Lazy merge_lazy(T l, T r) { return l + r; }
static T operate(T x, Lazy y, size_t) { return x + y; }
};
int main(void) {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
size_t T;
fin >> T;
while (T--) {
size_t n, m;
fin >> n >> m;
vector<Question> a(m);
for (auto& i : a) {
char c;
fin >> c;
i.c = c == '1';
fin >> i.l >> i.r;
--i.l;
}
size_t q;
fin >> q;
vector<pair<size_t, size_t>> ql(q);
for (auto& [l, r] : ql) fin >> l >> r, --l;
mrpython::lazy_segment_tree_from_node<AddMinNode>::type rgn(n, 0, 0);
mrpython::typical_segment_tree_min<size_t> rgr(n, n + 1);
vector<multiset<size_t>> rgv(n, {n + 1});
vector<size_t> ans(m);
for (size_t dl = 0, dr = 0; dl < m; ++dl) {
auto check = [&](Question const& t) {
if (t.c)
return !rgn.getd(t.l, t.r);
else {
size_t rl = rgn.find_last_left(t.l - 1, [](ui x) { return x == 0; }) +
1,
rr = rgn.find_first_right(t.r, [](ui x) { return x == 0; });
return rgr.get(rl, rr) > rr;
}
};
while (dr < m && check(a[dr])) {
if (a[dr].c) {
rgv[a[dr].l].emplace(a[dr].r);
rgr.set(a[dr].l, mrpython::const_function(*rgv[a[dr].l].begin()));
} else
rgn.set(a[dr].l, a[dr].r, 1);
++dr;
}
ans[dl] = dr;
if (a[dl].c) {
rgv[a[dl].l].erase(rgv[a[dl].l].find(a[dl].r));
rgr.set(a[dl].l, mrpython::const_function(*rgv[a[dl].l].begin()));
} else
rgn.set(a[dl].l, a[dl].r, -1);
}
for (auto [l, r] : ql) fout << (r <= ans[l] ? "YES\n" : "NO\n");
}
return 0;
}