题解:P12082 [Ynoi1998] Frühlingsbeginn
我们可以先把半开区间 \([l, r)\) 变成闭区间 \([l, r-1]\),坐标范围相应变成 \([1, n-1]\)。把操作离线下来,每个插入的区间就变成了一个带存在时间范围 \([s, e]\) 的静态区间。如果一直没被删,结束时间就是 \(m\)。
遇到询问 \((l, r, t)\) 时,显然只要把时刻 \(t\) 存在且完全被 \([l, r]\) 包含的区间全都拿出来求并集就行了。因为能用的区间只有这些,全选肯定是最优的,如果全选都盖不住 \([l, r]\),那任何子集也肯定盖不住。所以原问题直接等价于判断这些区间的并集是不是刚好等于 \([l, r]\)。
面对多组询问,单纯按时间或者端点排序都不太好搞包含关系,比较自然的想法是上猫树分治。在分治区间 \([L, R]\) 以及中点 \(\text{mid}\) 上,把每个询问挂在它第一次跨过 \(\text{mid}\) 的节点。操作区间同理,跨过 \(\text{mid}\) 的留在当前节点,否则丢到左右儿子去递归。这样做每个询问只会在一个节点被处理一次,每个操作区间最多下放 \(O(\log n)\) 次。
留在当前节点的询问都满足 \(l \le \text{mid} < r\)。对这些询问有用的区间分成三种:跨过 \(\text{mid}\) 的、全在左边的、全在右边的。我们先看跨过 \(\text{mid}\) 的部分。这些区间都包含 \(\text{mid}\) 和 \(\text{mid}+1\),所以只要把完全包含在询问里的有效区间拿出来,并集绝对是连续的一段 \([f, g]\)。如果根本没有符合要求的区间,就设 \(f=\text{mid}+1, g=\text{mid}\)。不然的话 \(f\) 就是这些区间里左端点的最小值,\(g\) 就是右端点的最大值。查的时候只考虑被询问包含且时刻 \(t\) 存活的区间。
为了快速求 \(f\) 和 \(g\),对于同一位置 \(x\) 开头的区间,想往左扩肯定找右端点尽量小的,所以维护 \(A_x = \min \{ r_i \mid l_i=x \}\),这里限制区间跨过 \(\text{mid}\) 且当前时间有效。找不到就设正无穷。那询问的 \(f\) 其实就是满足 \(A_x \le r\) 的最小的 \(x \in [l, \text{mid}]\)。右端点对称着来,维护 \(B_x = \max \{ l_i \mid r_i=x \}\),找不到设负无穷,那 \(g\) 就是满足 \(B_x \ge l\) 的最大的 \(x \in [\text{mid}+1, r]\)。因为有时间限制,直接把区间按存活时间做成加减事件,连同询问一起按时间跑扫描线。用两棵线段树分别维护 \(A_x\) 和 \(B_x\) 的最值,查 \(f\) 和 \(g\) 只要在线段树上二分就行了。
然后再处理两侧的区间,比如左侧。因为中间部分已经覆盖了 \([f, g]\),左边剩下的任务就是看看能不能从 \(l\) 一路连上 \(f-1\)。我们设 \(h\) 是从 \(l\) 开始向右连续覆盖到的第一个没被盖住的位置,一开始 \(h=l\)。把询问和左侧区间都按左端点从小到大扫。扫到某个询问的左端点时,把它丢进一棵以询问时间为下标的线段树里,初值设为 \(l\)。扫到区间 \([a, b]\) 时,它能且只能帮到询问时间在它存活时间 \([s, e]\) 内的询问,也就是给这些询问执行 \(h \gets \max(h, b+1)\)。线段树上直接做区间 chmax 即可。
这里有个需要注意的细节,如果扫到了某个左端点 \(x\),但线段树里某些询问的 \(h < x\),说明它们在 \(h\) 这里断档了,并且后面将要扫的区间左端点都 \(\ge x > h\),没救了。这时候直接把这些询问弹出来,它们的左侧延伸极限就是当前的 \(h\)。所以每次把左端点为 \(x\) 的询问加进线段树、并处理完左端点为 \(x\) 的所有区间后,要把线段树里仍满足 \(h=x\) 的询问(通过维护全局最小值)连带询问编号一起提出来处理掉。扫完之后还没被弹出的询问,说明它们能顺畅地一路盖到中点,极限设为 \(\text{mid}+1\)。这样左侧能接上中间的条件就是 \(h \ge f\)。
右侧操作完全是对称的,设 \(k\) 为从 \(r\) 往左走最后一个没被盖住的位置,初值 \(k=r\)。按右端点从大到小扫,遇到区间就执行区间 chmin 也就是 \(k \gets \min(k, a-1)\)。遇到 \(k > x\) 就提前弹出断档的询问,扫完后剩下的询问对应的 \(k\) 设为 \(\text{mid}\)。右侧接上的条件自然就是 \(k \le g\)。
综上,判断每个询问是否合法,只要看 \(h \ge f\) 且 \(k \le g\) 是否同时成立就行了。
时间复杂度 \(O(n + m \log n \log m)\),空间复杂度 \(O(n + m \log n)\)。
#include<bits/stdc++.h>
// #define int long long
using namespace std;
const int INF = 1e9;
namespace FastIO {
const int BUFSIZE = 1 << 20;
char ibuf[BUFSIZE];
int ipos = 0, ilen = 0;
inline char gc() {
if (ipos == ilen) {
ilen = fread(ibuf, 1, BUFSIZE, stdin);
ipos = 0;
if (ilen == 0) {
return EOF;
}
}
return ibuf[ipos++];
}
template<typename T>
inline void read(T &x) {
x = 0;
int f = 1;
char c = gc();
while (c != '-' && (c < '0' || c > '9')) {
c = gc();
}
if (c == '-') {
f = -1;
c = gc();
}
while (c >= '0' && c <= '9') {
x = x * 10 + (c - '0');
c = gc();
}
x *= f;
}
}
struct operation {
int l, r, ll, rr, nxt;
} op[500005];
struct Node {
int l, r, x, nxt;
} que[500005];
struct N {
int id, v;
} edge[11000005];
struct N1 {
int x, op, id, nxt;
} evnt[1000005];
const int MAXN = 1000005;
int n, m, headm[4000005], headq[4000005], headl[4000005], headr[4000005], coverl[500005], coverr[500005], arrivel[500005], arriver[500005], qtmp[500005], rk[500005], tim[500005], bit[500005], edgeid, bq[MAXN], bo[MAXN], bmark[MAXN], qnext[500005], onext[500005], bstamp, heade[4000005], taile[4000005], opnode[500005], eventid;
char hashq[4000005];
bool ans[500005];
namespace Heap {
struct HeapNode {
int l, r, d;
} hl[500005], hr[500005];
int rootl[1000005], rootr[1000005];
bool active[500005];
int mergeL(int x, int y) {
if (!x || !y) {
return x | y;
}
if (op[x].r > op[y].r || (op[x].r == op[y].r && x > y)) {
swap(x, y);
}
hl[x].r = mergeL(hl[x].r, y);
if (hl[hl[x].l].d < hl[hl[x].r].d) {
swap(hl[x].l, hl[x].r);
}
hl[x].d = hl[hl[x].r].d + 1;
return x;
}
int mergeR(int x, int y) {
if (!x || !y) {
return x | y;
}
if (op[x].l < op[y].l || (op[x].l == op[y].l && x < y)) {
swap(x, y);
}
hr[x].r = mergeR(hr[x].r, y);
if (hr[hr[x].l].d < hr[hr[x].r].d) {
swap(hr[x].l, hr[x].r);
}
hr[x].d = hr[hr[x].r].d + 1;
return x;
}
int getmin(int x) {
while (rootl[x] && !active[rootl[x]]) {
int id = rootl[x];
rootl[x] = mergeL(hl[id].l, hl[id].r);
}
return rootl[x] ? op[rootl[x]].r : INF;
}
int getmax(int x) {
while (rootr[x] && !active[rootr[x]]) {
int id = rootr[x];
rootr[x] = mergeR(hr[id].l, hr[id].r);
}
return rootr[x] ? op[rootr[x]].l : 0;
}
};
int visl[1000005], visr[1000005], listl[500005], listr[500005], stamp, cntl, cntr;
void addedge(int &numid, int id) {
edge[++edgeid] = {id, numid};
numid = edgeid;
}
void addevent(int p, int x, int typ, int id) {
evnt[++eventid] = {x, typ, id, 0};
if (taile[p]) {
evnt[taile[p]].nxt = eventid;
} else {
heade[p] = eventid;
}
taile[p] = eventid;
}
void getop(int id) {
int p = 1, l = 1, r = n;
while (true) {
if (l == r) {
op[id].nxt = headm[p];
headm[p] = id;
opnode[id] = p;
addevent(p, op[id].ll, 1, id);
return;
}
int mid = (l + r) >> 1;
if (op[id].l <= mid && mid < op[id].r) {
op[id].nxt = headm[p];
headm[p] = id;
opnode[id] = p;
addevent(p, op[id].ll, 1, id);
return;
}
if (op[id].r <= mid) {
addedge(headl[p], id);
r = mid;
p *= 2;
} else {
addedge(headr[p], id);
l = mid + 1;
p = p * 2 + 1;
}
}
}
void getque(int id) {
int p = 1, l = 1, r = n;
while (true) {
hashq[p] = 1;
if (l == r) {
que[id].nxt = headq[p];
headq[p] = id;
addevent(p, que[id].x, 0, id);
return;
}
int mid = (l + r) >> 1;
if (que[id].l <= mid && mid < que[id].r) {
que[id].nxt = headq[p];
headq[p] = id;
addevent(p, que[id].x, 0, id);
return;
}
if (que[id].r <= mid) {
r = mid;
p *= 2;
} else {
l = mid + 1;
p = p * 2 + 1;
}
}
}
namespace LTree {
int minn[4000005];
void buildtree(int p, int l, int r) {
minn[p] = INF;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
buildtree(p * 2, l, mid);
buildtree(p * 2 + 1, mid + 1, r);
}
void change(int p, int l, int r, int x, int d) {
if (l == r) {
minn[p] = d;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) {
change(p * 2, l, mid, x, d);
} else {
change(p * 2 + 1, mid + 1, r, x, d);
}
minn[p] = min(minn[p * 2], minn[p * 2 + 1]);
}
int get(int p, int l, int r, int ll, int rr, int d) {
if (r < ll || l > rr || minn[p] > d) {
return -1;
}
if (l == r) {
return l;
}
int mid = (l + r) >> 1, pos = get(p * 2, l, mid, ll, rr, d);
if (pos != -1) {
return pos;
}
return get(p * 2 + 1, mid + 1, r, ll, rr, d);
}
};
namespace RTree {
int maxn[4000005];
void buildtree(int p, int l, int r) {
maxn[p] = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
buildtree(p * 2, l, mid);
buildtree(p * 2 + 1, mid + 1, r);
}
void change(int p, int l, int r, int x, int d) {
if (l == r) {
maxn[p] = d;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) {
change(p * 2, l, mid, x, d);
} else {
change(p * 2 + 1, mid + 1, r, x, d);
}
maxn[p] = max(maxn[p * 2], maxn[p * 2 + 1]);
}
int get(int p, int l, int r, int ll, int rr, int x) {
if (r < ll || l > rr || maxn[p] < x) {
return -1;
}
if (l == r) {
return l;
}
int mid = (l + r) >> 1, pos = get(p * 2 + 1, mid + 1, r, ll, rr, x);
if (pos != -1) {
return pos;
}
return get(p * 2, l, mid, ll, rr, x);
}
};
namespace L {
pair<int, int> minn[2000005];
int tag[2000005];
void buildtree(int p, int l, int r) {
minn[p] = {INF, 0};
tag[p] = 0;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
buildtree(p * 2, l, mid);
buildtree(p * 2 + 1, mid + 1, r);
}
void pushdown(int p) {
if (tag[p]) {
if (minn[p * 2].first != INF) {
minn[p * 2].first = max(minn[p * 2].first, tag[p]);
tag[p * 2] = max(tag[p * 2], tag[p]);
}
if (minn[p * 2 + 1].first != INF) {
minn[p * 2 + 1].first = max(minn[p * 2 + 1].first, tag[p]);
tag[p * 2 + 1] = max(tag[p * 2 + 1], tag[p]);
}
tag[p] = 0;
}
}
void change(int p, int l, int r, int ll, int rr, int x) {
if (ll > rr || minn[p].first >= x) {
return;
}
if (ll <= l && r <= rr) {
minn[p].first = max(minn[p].first, x);
tag[p] = max(tag[p], x);
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (ll <= mid) {
change(p * 2, l, mid, ll, rr, x);
}
if (rr > mid) {
change(p * 2 + 1, mid + 1, r, ll, rr, x);
}
minn[p] = min(minn[p * 2], minn[p * 2 + 1]);
}
void update(int p, int l, int r, int x, pair<int, int> d) {
if (l == r) {
minn[p] = d;
tag[p] = 0;
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (x <= mid) {
update(p * 2, l, mid, x, d);
} else {
update(p * 2 + 1, mid + 1, r, x, d);
}
minn[p] = min(minn[p * 2], minn[p * 2 + 1]);
}
void erase(int p, int l, int r, int x) {
if (minn[p].first != x) {
return;
}
if (l == r) {
arrivel[minn[p].second] = x;
minn[p] = {INF, 0};
tag[p] = 0;
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (minn[p * 2].first == x) {
erase(p * 2, l, mid, x);
}
if (minn[p * 2 + 1].first == x) {
erase(p * 2 + 1, mid + 1, r, x);
}
minn[p] = min(minn[p * 2], minn[p * 2 + 1]);
}
};
namespace R {
pair<int, int> maxn[2000005];
int tag[2000005];
void buildtree(int p, int l, int r) {
maxn[p] = {-INF, 0};
tag[p] = INF;
if (l == r) {
return;
}
int mid = (l + r) >> 1;
buildtree(p * 2, l, mid);
buildtree(p * 2 + 1, mid + 1, r);
}
void pushdown(int p) {
if (tag[p] != INF) {
if (maxn[p * 2].first != -INF) {
maxn[p * 2].first = min(maxn[p * 2].first, tag[p]);
tag[p * 2] = min(tag[p * 2], tag[p]);
}
if (maxn[p * 2 + 1].first != -INF) {
maxn[p * 2 + 1].first = min(maxn[p * 2 + 1].first, tag[p]);
tag[p * 2 + 1] = min(tag[p * 2 + 1], tag[p]);
}
tag[p] = INF;
}
}
void change(int p, int l, int r, int ll, int rr, int x) {
if (ll > rr || maxn[p].first <= x) {
return;
}
if (ll <= l && r <= rr) {
maxn[p].first = min(maxn[p].first, x);
tag[p] = min(tag[p], x);
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (ll <= mid) {
change(p * 2, l, mid, ll, rr, x);
}
if (rr > mid) {
change(p * 2 + 1, mid + 1, r, ll, rr, x);
}
maxn[p] = max(maxn[p * 2], maxn[p * 2 + 1]);
}
void update(int p, int l, int r, int x, pair<int, int> d) {
if (l == r) {
maxn[p] = d;
tag[p] = INF;
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (x <= mid) {
update(p * 2, l, mid, x, d);
} else {
update(p * 2 + 1, mid + 1, r, x, d);
}
maxn[p] = max(maxn[p * 2], maxn[p * 2 + 1]);
}
void erase(int p, int l, int r, int x) {
if (maxn[p].first != x) {
return;
}
if (l == r) {
arriver[maxn[p].second] = x;
maxn[p] = {-INF, 0};
tag[p] = INF;
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (maxn[p * 2].first == x) {
erase(p * 2, l, mid, x);
}
if (maxn[p * 2 + 1].first == x) {
erase(p * 2 + 1, mid + 1, r, x);
}
maxn[p] = max(maxn[p * 2], maxn[p * 2 + 1]);
}
};
void setl(int x, int v) {
if (visl[x] != stamp) {
visl[x] = stamp;
listl[++cntl] = x;
}
LTree::change(1, 1, n, x, v);
}
void setr(int x, int v) {
if (visr[x] != stamp) {
visr[x] = stamp;
listr[++cntr] = x;
}
RTree::change(1, 1, n, x, v);
}
int lowbit(int x) {
return x & (-x);
}
void change(int x, int y) {
for (; x <= m; x += lowbit(x)) {
bit[x] += y;
}
}
int getsum(int x) {
int res = 0;
for (; x; x -= lowbit(x)) {
res += bit[x];
}
return res;
}
void worklf(int p) {
for (int i = headm[p]; i; i = op[i].nxt) {
change(op[i].ll, 1);
change(op[i].rr + 1, -1);
}
for (int i = headq[p]; i; i = que[i].nxt) {
ans[i] = (getsum(que[i].x) > 0);
}
for (int i = headm[p]; i; i = op[i].nxt) {
change(op[i].ll, -1);
change(op[i].rr + 1, 1);
}
}
void solve(int p, int l, int r) {
if (!hashq[p]) {
return;
}
if (l == r) {
worklf(p);
return;
}
int mid = (l + r) >> 1, len = 0, siz = 0;
for (int i = headq[p]; i; i = que[i].nxt) {
qtmp[++len] = i;
}
if (len) {
reverse(qtmp + 1, qtmp + len + 1);
for (int i = 1; i <= len; i = -~i) {
tim[i] = que[qtmp[i]].x;
rk[qtmp[i]] = i;
arrivel[qtmp[i]] = mid + 1;
arriver[qtmp[i]] = mid;
}
stamp ++;
cntl = cntr = 0;
for (int i = heade[p]; i; i = evnt[i].nxt) {
int id = evnt[i].id;
if (evnt[i].op == 1) {
int oldl = Heap::getmin(op[id].l), oldr = Heap::getmax(op[id].r);
Heap::active[id] = 1;
Heap::rootl[op[id].l] = Heap::mergeL(Heap::rootl[op[id].l], id);
Heap::rootr[op[id].r] = Heap::mergeR(Heap::rootr[op[id].r], id);
if (op[id].r < oldl) {
setl(op[id].l, op[id].r);
}
if (op[id].l > oldr) {
setr(op[id].r, op[id].l);
}
} else if (evnt[i].op == -1) {
int oldl = Heap::getmin(op[id].l), oldr = Heap::getmax(op[id].r);
Heap::active[id] = 0;
int newl = Heap::getmin(op[id].l), newr = Heap::getmax(op[id].r);
if (oldl == op[id].r) {
setl(op[id].l, newl);
}
if (oldr == op[id].l) {
setr(op[id].r, newr);
}
} else {
coverl[id] = LTree::get(1, 1, n, que[id].l, mid, que[id].r);
coverr[id] = RTree::get(1, 1, n, mid + 1, que[id].r, que[id].l);
if (coverl[id] == -1) {
coverl[id] = mid + 1;
}
if (coverr[id] == -1) {
coverr[id] = mid;
}
}
}
for (int i = 1; i <= cntl; i = -~i) {
LTree::change(1, 1, n, listl[i], INF);
}
for (int i = 1; i <= cntr; i = -~i) {
RTree::change(1, 1, n, listr[i], 0);
}
bstamp = -~bstamp ;
for (int k = 1; k <= len; k = -~k) {
int id = qtmp[k], x = que[id].l;
if (bmark[x] != bstamp) {
bmark[x] = bstamp;
bq[x] = bo[x] = 0;
}
qnext[id] = bq[x];
bq[x] = id;
}
for (int k = headl[p]; k; k = edge[k].v) {
int id = edge[k].id, x = op[id].l;
if (bmark[x] != bstamp) {
bmark[x] = bstamp;
bq[x] = bo[x] = 0;
}
onext[id] = bo[x];
bo[x] = id;
}
L::buildtree(1, 1, len);
for (int x = l; x <= mid; x ++) {
if (bmark[x] == bstamp) {
for (int id = bq[x]; id; id = qnext[id]) {
L::update(1, 1, len, rk[id], {x, id});
}
for (int id = bo[x]; id; id = onext[id]) {
L::change(1, 1, len, lower_bound(tim + 1, tim + 1 + len, op[id].ll) - tim, upper_bound(tim + 1, tim + 1 + len, op[id].rr) - tim - 1, op[id].r + 1);
}
}
if (L::minn[1].first == x) {
L::erase(1, 1, len, x);
}
}
bstamp = -~bstamp;
for (int k = 1; k <= len; k = -~k) {
int id = qtmp[k], x = que[id].r;
if (bmark[x] != bstamp) {
bmark[x] = bstamp;
bq[x] = bo[x] = 0;
}
qnext[id] = bq[x];
bq[x] = id;
}
for (int k = headr[p]; k; k = edge[k].v) {
int id = edge[k].id, x = op[id].r;
if (bmark[x] != bstamp) {
bmark[x] = bstamp;
bq[x] = bo[x] = 0;
}
onext[id] = bo[x];
bo[x] = id;
}
R::buildtree(1, 1, len);
for (int x = r; x > mid; x --) {
if (bmark[x] == bstamp) {
for (int id = bq[x]; id; id = qnext[id]) {
R::update(1, 1, len, rk[id], {x, id});
}
for (int id = bo[x]; id; id = onext[id]) {
R::change(1, 1, len, lower_bound(tim + 1, tim + 1 + len, op[id].ll) - tim, upper_bound(tim + 1, tim + 1 + len, op[id].rr) - tim - 1, op[id].l - 1);
}
}
if (R::maxn[1].first == x) {
R::erase(1, 1, len, x);
}
}
for (int k = 1; k <= len; k = -~k) {
int id = qtmp[k];
ans[id] = (arrivel[id] >= coverl[id] && arriver[id] <= coverr[id]);
}
}
if (hashq[p * 2]) {
solve(p * 2, l, mid);
}
if (hashq[p * 2 + 1]) {
solve(p * 2 + 1, mid + 1, r);
}
}
signed main() {
int cnt = 0;
FastIO::read(n);
FastIO::read(m);
n --;
for (int i = 1; i <= m; i = -~i) {
int opt, x, y;
FastIO::read(opt);
FastIO::read(x);
if (opt == 1) {
FastIO::read(y);
op[i] = {x, y - 1, i, m, 0};
getop(i);
} else if (opt == 2) {
op[x].rr = i - 1;
addevent(opnode[x], i, -1, x);
} else {
FastIO::read(y);
que[++cnt] = {x, y - 1, i, 0};
getque(cnt);
}
}
for (int i = 1; i <= m; i = -~i) {
if (opnode[i] && op[i].rr == m) {
addevent(opnode[i], m + 1, -1, i);
}
}
LTree::buildtree(1, 1, n);
RTree::buildtree(1, 1, n);
solve(1, 1, n);
for (int i = 1; i <= cnt; i = -~i) {
if (ans[i]) {
puts("Y");
} else {
puts("N");
}
}
return 0;
}
本文来自博客园,作者:xuyifei0302,转载请注明原文链接:https://www.cnblogs.com/xuyifei0302/p/23629059

浙公网安备 33010602011771号