# P11695 [JRKSJ ExR] 昼寝 题解
P11695 [JRKSJ ExR] 昼寝 题解
数据加强版:P12082 [Ynoi1998] Frühlingsbeginn
线段树神题
题目大意
给定 \(m\) 个操作,支持插入一个区间、删除一个区间、判断是否存在一个区间子集的并集恰好等于给定区间。
思路
以下题解默认 \(l, r\) 为闭区间。
考虑猫树分治。在分治至区间 \([l, r]\) 时,处理所有跨过 \(mid \rightarrow mid + 1\) 的查询区间。 将修改区间 \([L, R]\) 分成三类处理:
首先处理第一类。
由于每个修改区间都跨过 \(mid \rightarrow mid + 1\) ,故这些区间的每个子集形成的并集都必定可以形成一个无间断的大区间,即我们可以任意选择这些区间。在此前提下,我们分别考虑每一个查询区间 \([l_i, r_i]\) ,显然,我们可以分别贪心地找出最小的 \(z\) 和最大的 \(y\) ,满足:
容易发现 \(z,\ y\) 是互不影响的,并且形式上是对称的,可以类似处理,故在此只讨论 \(z\) 的求解。为方便思考,我们不妨先忽略时间维度。考虑对所有修改区间,在每个左端点维护右端点的最小值,可以发现 \(z\) 其实就是查找 \([l,\ mid]\) 之间第一个小于等于 \(r_i\) 的数,这可以使用线段树二分简单地维护。那如果加上时间维度呢?容易发现如果我们按照时间顺序遍历,这实际上就是增加了一个删除操作,我们可以通过实现以下三个操作来维护:单点 chmn 、撤销一个 chmn 、维护区间最小值。一个较简单的处理方式是对线段树上每个叶子维护一个 multiset 来实现插入删除。
接着考虑第二、三类区间。由于我们已经对每个查询区间 \([l_i, r_i]\) 求出了极大子区间 \([z,\ y]\) ,故我们只需要设法用第二类填满 \([l_i,\ z - 1]\) ,用第三类 \([y + 1,\ r_i]\) 即可。可以发现第二类与第三类的形式极其对称,可以用类似的处理方法求解,故在此只讨论第二类。
对于第二类,为了填满 \([l_i,\ z - 1]\) ,我们可以为每个查询区间找到最大的 \(el_i\) ,满足 \([l_i,\ el_i]\) 可以恰好被填满。我们不妨依旧先忽略时间维度思考。稍加思索不难想到,我们可以按照左端点从小到大遍历每个修改区间 \([L,\ R]\) ,若 \(el_i + 1 >= L\) ,则 \(el_i\ \leftarrow max(el_i,\ R)\) ;否则 \(el_i\) 将无法再被更新,可以立即跳出循环。那么,加上时间呢?由于我们已经有了遍历顺序,显然无法像第一类一样直接按时间遍历。但仔细观察,发现我们之前竟然只同时维护了一个值,还有很大的操作空间,故可以自然想到把时间维度拍到序列上,序列中每个点表示该时刻的询问的 \(el_i\) 。在遍历到一个左端点 \(j\) 时,首先在时间序列上激活左端点为 \(j\) 的询问序列的询问时刻,并将其 \(el_i\) 初始化为 \(l - 1\) ;然后对于左端点为 \(j\) 的修改区间,对其存在的时间区间内的所有已激活点做 chmx ;最后取消激活所有 \(el_i \le j - 1\) 的询问,并固定其 \(el_i\) 。整理一下,可以发现我们需要实现以下东西:激活一个下标、取消激活一个下标同时记录答案、区间 chmx 。这些操作也可以用我们强大的线段树解决。
最后,一个查询区间答案为 true 当且仅当 \(z\le el_i + 1\ \wedge \ er_i + 1 \le y\) 。
时间复杂度 \(\mathcal{O}(n\log^2n)\) ,空间复杂度 \(\mathcal{O}(n)\) 。
当然,本题思路虽复杂,但实现也不简单。在此给出一些好写的建议:
- 可以把所有区间分别挂在左右端点上,方便遍历。
- 虽然理论上需要实现四棵线段树,但实际上可以合并一些相同操作,形式上只写两棵,代码更短也方便调试。
Code
#include<bits/stdc++.h>
#define LL long long
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PIL pair<int, LL>
#define PII pair<int, int>
using namespace std;
const int N = 1e6, M = 5e5, inf = 1e9;
int n, m;
struct SGT1 {
struct node {
int mx, mi;
}tree[N << 2];
multiset<int> s[N + 5];
#define lc (id << 1)
#define rc (lc + 1)
#define mid ((l + r) >> 1)
inline void pushup(int id) {
tree[id].mi = min(tree[lc].mi, tree[rc].mi);
tree[id].mx = max(tree[lc].mx, tree[rc].mx);
}
void build(int l = 1, int r = n, int id = 1) {
tree[id].mi = inf; tree[id].mx = 0;
if (l == r) return;
build(l, mid, lc); build(mid + 1, r, rc);
}
void update(const int pos, const int v, int l = 1, int r = n, int id = 1) {
if (l == r) return s[l].insert(v), tree[id].mi = *s[l].begin(), tree[id].mx = *s[l].rbegin(), void(0);
if (pos <= mid) update(pos, v, l, mid, lc);
else update(pos, v, mid + 1, r, rc);
pushup(id);
}
void del(const int pos, const int v, int l = 1, int r = n, int id = 1) {
if (l == r) {
s[l].erase(s[l].find(v));
if (!s[l].empty()) tree[id].mi = *s[l].begin(), tree[id].mx = *s[l].rbegin();
else tree[id].mi = inf, tree[id].mx = 0;
return;
}
if (pos <= mid) del(pos, v, l, mid, lc);
else del(pos, v, mid + 1, r, rc);
pushup(id);
}
int getfst(const int z, const int y, const int k, int l = 1, int r = n, int id = 1) {
if (z > y || y < l || r < z) return y + 1;
if (l == r) return l;
int ret = (tree[lc].mi <= k ? getfst(z, y, k, l, mid, lc) : y + 1);
if (ret != y + 1) return ret;
return (tree[rc].mi <= k ? getfst(z, y, k, mid + 1, r, rc) : y + 1);
}
int getlst(const int z, const int y, const int k, int l = 1, int r = n, int id = 1) {
if (z > y || y < l || r < z) return z - 1;
if (l == r) return l;
int ret = (tree[rc].mx >= k ? getlst(z, y, k, mid + 1, r, rc) : z - 1);
if (ret != z - 1) return ret;
return (tree[lc].mx >= k ? getlst(z, y, k, l, mid, lc) : z - 1);
}
#undef lc
#undef rc
#undef mid
}sgt1;
struct SGT2 {
struct node {
PII mi, mx; int up, dw, id;
}tree[M << 2];
#define lc (id << 1)
#define rc (lc + 1)
#define mid ((l + r) >> 1)
inline void pushup(int id) {
tree[id].mi = min(tree[lc].mi, tree[rc].mi);
tree[id].mx = max(tree[lc].mx, tree[rc].mx);
tree[id].id = max(tree[lc].id, tree[rc].id);
}
inline void chmi(const int id, const int v) {
if (!tree[id].id) return;
tree[id].mi.first = min(v, tree[id].mi.first); tree[id].mx.first = min(tree[id].mx.first, v); tree[id].up = min(tree[id].up, v);
}
inline void chmx(const int id, const int v) {
if (!tree[id].id) return;
tree[id].mi.first = max(v, tree[id].mi.first); tree[id].mx.first = max(tree[id].mx.first, v); tree[id].dw = max(tree[id].dw, v);
}
inline void push_down(int id) {
if (tree[id].up < inf) {
int& v = tree[id].up;
chmi(lc, v); chmi(rc, v);
v = inf;
}
if (tree[id].dw < inf) {
int& v = tree[id].dw;
chmx(lc, v); chmx(rc, v);
v = 0;
}
}
void build(int l = 1, int r = m, int id = 1) {
tree[id].mi = {inf, l}; tree[id].mx = {0, r}; tree[id].up = inf; tree[id].dw = 0;
if (l == r) return;
build(l, mid, lc); build(mid + 1, r, rc);
}
void chmin(const int z, const int y, const int v, int l = 1, int r = m, int id = 1) {
if (!tree[id].id || z > y || y < l || r < z) return;
if (z <= l && r <= y) return chmi(id, v);
push_down(id);
chmin(z, y, v, l, mid, lc); chmin(z, y, v, mid + 1, r, rc);
pushup(id);
}
void chmax(const int z, const int y, const int v, int l = 1, int r = m, int id = 1) {
if (!tree[id].id || z > y || y < l || r < z) return;
if (z <= l && r <= y) return chmx(id, v);
push_down(id);
chmax(z, y, v, l, mid, lc); chmax(z, y, v, mid + 1, r, rc);
pushup(id);
}
void add(const int pos, const int v, int l = 1, int r = m, int id = 1) {
if (l == r) return tree[id].id = l, tree[id].mi.first = tree[id].mx.first = v, void(0);
push_down(id);
if (pos <= mid) add(pos, v, l, mid, lc);
else add(pos, v, mid + 1, r, rc);
pushup(id);
}
void del(const int pos, int l = 1, int r = m, int id = 1) {
if (l == r) return tree[id].id = 0, tree[id].mi.first = inf, tree[id].mx.first = 0, void(0);
push_down(id);
if (pos <= mid) del(pos, l, mid, lc);
else del(pos, mid + 1, r, rc);
pushup(id);
}
#undef lc
#undef rc
#undef mid
}sgt2;
struct BIT {
int s[M + 5];
inline int lowbit(const int i) { return i & (-i); }
inline void update(int i, const int v) { for (; i <= m + 2; i += lowbit(i)) s[i] += v; }
inline int query(int i) {
int ret = 0;
for (; i > 0; i -= lowbit(i)) ret += s[i];
return ret;
}
}w;
int el[M + 5], er[M + 5];
bool ans[M + 5];
struct Segment {
int l, r, tl, tr, id;
};
inline bool cmp3(const Segment a, const Segment b) { return a.tl < b.tl; }
vector<Segment> upd[2][N + 5], qy[2][N + 5];
Segment qry[M + 5], mdy[M * 2 + 5];
PII pos[2][M + 5];
void dfs(int l = 1, int r = n) {
if (l == r) {
for (auto i : upd[0][l]) if (i.r == l) { w.update(i.tl, 1); w.update(i.tr, -1); }
for (auto i : qy[0][l]) if (i.r == r) { ans[i.id] = (w.query(i.tl) > 0); }
for (auto i : upd[0][l]) if (i.r == l) { w.update(i.tl, -1); w.update(i.tr, 1); }
return;
}
int mid = (l + r) >> 1;
dfs(l, mid); dfs(mid + 1, r);
for (int i = l; i <= mid; i++) {
for (auto j : qy[0][i]) if (mid < j.r && j.r <= r) sgt2.add(j.tl, j.l - 1);
for (auto j : upd[0][i]) if (j.r <= mid) sgt2.chmax(j.tl, j.tr - 1, j.r);
while (sgt2.tree[1].mi.first <= i - 1) {
int p = sgt2.tree[1].mi.second;
Segment now = qy[0][pos[0][p].first][pos[0][p].second];
el[now.id] = sgt2.tree[1].mi.first;
sgt2.del(p);
}
}
while (sgt2.tree[1].mi.first < inf) {
int p = sgt2.tree[1].mi.second;
Segment now = qy[0][pos[0][p].first][pos[0][p].second];
el[now.id] = sgt2.tree[1].mi.first;
sgt2.del(p);
}
for (int i = r; i > mid; i--) {
for (auto j : qy[1][i]) if (l <= j.l && j.l <= mid) sgt2.add(j.tl, j.r + 1);
for (auto j : upd[1][i]) if (mid < j.l) sgt2.chmin(j.tl, j.tr - 1, j.l);
while (sgt2.tree[1].mx.first >= i + 1) {
int p = sgt2.tree[1].mx.second;
Segment now = qy[1][pos[1][p].first][pos[1][p].second];
er[now.id] = sgt2.tree[1].mx.first;
sgt2.del(p);
}
}
while (sgt2.tree[1].mx.first > 0) {
int p = sgt2.tree[1].mx.second;
Segment now = qy[1][pos[1][p].first][pos[1][p].second];
er[now.id] = sgt2.tree[1].mx.first;
sgt2.del(p);
}
int ctq = 0, ctm = 0;
for (int i = l; i <= mid; i++) {
for (auto j : qy[0][i]) if (mid < j.r && j.r <= r) qry[++ctq] = j;
for (auto j : upd[0][i]) if (mid < j.r && j.r <= r) {
mdy[++ctm] = j; mdy[ctm].id = 1; mdy[ctm].tr = mdy[ctm].tl;
mdy[++ctm] = j; mdy[ctm].id = -1; mdy[ctm].tl = mdy[ctm].tr;
}
}
sort(qry + 1, qry + ctq + 1, cmp3); sort(mdy + 1, mdy + ctm + 1, cmp3);
int p = 1;
for (int i = 1; i <= ctq; i++) {
while (p <= ctm && mdy[p].tl <= qry[i].tl) {
if (mdy[p].id == 1) sgt1.update(mdy[p].l, mdy[p].r), sgt1.update(mdy[p].r, mdy[p].l);
else sgt1.del(mdy[p].l, mdy[p].r), sgt1.del(mdy[p].r, mdy[p].l);
p++;
}
int z = sgt1.getfst(qry[i].l, mid, qry[i].r), y = sgt1.getlst(mid + 1, qry[i].r, qry[i].l);
int h = qry[i].id;
ans[h] = (z <= el[h] + 1 && er[h] - 1 <= y);
}
p--;
while (p >= 1) {
if (mdy[p].id == -1) sgt1.update(mdy[p].l, mdy[p].r), sgt1.update(mdy[p].r, mdy[p].l);
else sgt1.del(mdy[p].l, mdy[p].r), sgt1.del(mdy[p].r, mdy[p].l);
p--;
}
}
inline void read(int& x) {
x = 0; char c = getchar();
while (c < '0' || '9' < c) c = getchar();
while ('0' <= c && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
}
inline void solve() {
read(n); read(m); int q = 0; n--;
for (int i = 1; i <= m; i++) {
int o; read(o);
if (o == 1) {
int l, r; read(l); read(r); r--;
pos[0][i] = {l, upd[0][l].size()};
pos[1][i] = {r, upd[1][r].size()};
upd[0][l].push_back({l, r, i, m + 1, 0});
upd[1][r].push_back({l, r, i, m + 1, 0});
} else if (o == 2) {
int t; read(t);
upd[0][pos[0][t].first][pos[0][t].second].tr = i;
upd[1][pos[1][t].first][pos[1][t].second].tr = i;
} else {
int l, r; read(l); read(r); r--;
pos[0][i] = {l, qy[0][l].size()};
pos[1][i] = {r, qy[1][r].size()};
qy[0][l].push_back({l, r, i, i, ++q});
qy[1][r].push_back({l, r, i, i, q});
}
}
sgt1.build(); sgt2.build();
dfs();
for (int i = 1; i <= q; i++) printf("%c\n", (ans[i] ? 'Y' : 'N'));
}
signed main() {
int tt = 1;
while (tt--) solve();
return 0;
}
浙公网安备 33010602011771号