CodeForces 983D Arkady and Rectangles
Yet Another God Problem
思路
对于这种矩形覆盖的问题,一般考虑扫描线+线段树。
首先离散化坐标。扫描 \(x\) 轴,对 \(y\) 轴建线段树。离散化后设 \(y\) 轴有 \(tot\) 个端点,则有 \(tot - 1\) 个区间,在线段树上每个叶子节点维护的实际上是每个线段的信息。
线段树上每个节点维护三个值:
-
\(mx\) 表示在当前区间内可以看到的且还未被统计进最终答案的最大的颜色。
-
\(mn\) 表示在当前区间内可以看到的且已经被统计进最终答案的最小的颜色。这意味着如果记 \(cov_i\) 为当前覆盖住了第 \(i\) 个区间的颜色,则 \(mn_{rt}\) 为 \(\min\limits_{k=l}^r cov_k\),其中 \([l,r]\) 为结点 \(rt\) 管辖的区间范围。
-
再维护一个
set
表示覆盖住了当前区间的所有颜色 (不一定是最大的)。
pushup
时,首先先将 \(mx\) 对儿子的 \(mx\) 取最大值,\(mn\) 对儿子的 \(mn\) 取最小值。然后取出当前结点的 set
的最大值,记为 \(col\),若 \(col\) 未被统计进最终答案,则 \(mx \gets \max(mx,col)\),否则 \(mn \gets \max(mn,col)\)。根据定义,若当前结点的 \(mn > mx\),说明剩下的未被统计进答案的颜色没有能够覆盖住其他所有颜色的颜色了,\(mx \gets 0\)。
扫描到 \(x\) 轴的每个端点时,先添加以当前端点为起始点的线段,删除以当前端点为终止点的线段。然后,若线段树上全局的 \(mx\) 不为 \(0\),则将 \(mx\) 统计进答案,且在线段树上重新计算有关这个线段的所有信息。
时间复杂度 \(O(n \log^2 n)\)。
代码
code
/*
p_b_p_b txdy
AThousandMoon txdy
AThousandSuns txdy
hxy txdy
*/
#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int maxn = 200100;
int n, lsh1[maxn], tot1, lsh2[maxn], tot2;
bool vis[maxn];
struct node {
int l, r, col, op;
node() {}
node(int a, int b, int c, int d) : l(a), r(b), col(c), op(d) {}
};
vector<node> vc[maxn];
struct rect {
int xa, ya, xb, yb;
rect() {}
rect(int a, int b, int c, int d) : xa(a), ya(b), xb(c), yb(d) {}
} a[maxn];
struct treenode {
set<int> st;
int mx, mn;
} tree[maxn << 2];
void pushup(int x, int l, int r) {
if (l == r) {
tree[x].mx = tree[x].mn = 0;
} else {
tree[x].mx = max(tree[x << 1].mx, tree[x << 1 | 1].mx);
tree[x].mn = min(tree[x << 1].mn, tree[x << 1 | 1].mn);
}
if (tree[x].st.size()) {
int tmp = *(--tree[x].st.end());
if (vis[tmp]) {
tree[x].mn = max(tree[x].mn, tmp);
} else {
tree[x].mx = max(tree[x].mx, tmp);
}
}
if (tree[x].mn > tree[x].mx) {
tree[x].mx = 0;
}
}
void update(int rt, int l, int r, int ql, int qr, int x, int op) {
if (ql > qr) {
return;
}
if (ql <= l && r <= qr) {
if (op == 1) {
tree[rt].st.insert(x);
} else if (op == 2) {
tree[rt].st.erase(x);
}
pushup(rt, l, r);
return;
}
int mid = (l + r) >> 1;
if (ql <= mid) {
update(rt << 1, l, mid, ql, qr, x, op);
}
if (qr > mid) {
update(rt << 1 | 1, mid + 1, r, ql, qr, x, op);
}
pushup(rt, l, r);
}
void solve() {
scanf("%d", &n);
for (int i = 1, xa, ya, xb, yb; i <= n; ++i) {
scanf("%d%d%d%d", &xa, &ya, &xb, &yb);
lsh1[++tot1] = xa;
lsh1[++tot1] = xb;
lsh2[++tot2] = ya;
lsh2[++tot2] = yb;
a[i] = rect(xa, ya, xb, yb);
}
sort(lsh1 + 1, lsh1 + tot1 + 1);
sort(lsh2 + 1, lsh2 + tot2 + 1);
tot1 = unique(lsh1 + 1, lsh1 + tot1 + 1) - lsh1 - 1;
tot2 = unique(lsh2 + 1, lsh2 + tot2 + 1) - lsh2 - 1;
for (int i = 1; i <= n; ++i) {
a[i].xa = lower_bound(lsh1 + 1, lsh1 + tot1 + 1, a[i].xa) - lsh1;
a[i].xb = lower_bound(lsh1 + 1, lsh1 + tot1 + 1, a[i].xb) - lsh1;
a[i].ya = lower_bound(lsh2 + 1, lsh2 + tot2 + 1, a[i].ya) - lsh2;
a[i].yb = lower_bound(lsh2 + 1, lsh2 + tot2 + 1, a[i].yb) - lsh2;
vc[a[i].xa].pb(node(a[i].ya, a[i].yb, i, 1));
vc[a[i].xb].pb(node(a[i].ya, a[i].yb, i, 2));
}
int ans = 1;
for (int i = 1; i <= tot1; ++i) {
for (node p : vc[i]) {
update(1, 1, tot2 - 1, p.l, p.r - 1, p.col, p.op);
}
while (tree[1].mx) {
vis[tree[1].mx] = 1;
++ans;
update(1, 1, tot2 - 1, a[tree[1].mx].ya, a[tree[1].mx].yb - 1, 0, 0);
}
}
printf("%d\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}