洛谷P7312 Sunčanje 题解
题面
传送门
有 个矩形,给定在平面直角坐标系中的左下角、右上角坐标,以及放置的顺序。求每个矩形是否有部分被覆盖。
,坐标为不超过 的非负整数。保证矩形有面积。
题解
这是一道CDQ分治的好题。
分析此问题,可以发现是一个五维偏序,难以直接求解。但是由于此题中维度之间构成特殊的性质,限制了五维偏序的普适性,使得可以降维。有两种解法。
解法一
容斥原理。
对于一个矩形,可能交它的一定在它上面。则可以统计它上面有几个矩形不能交它,若不等于上面矩形的总数,则被覆盖。
对于一个矩形,考察它上面的所有矩形(偏序问题,此处有一维)。将此矩形的四边呈直线延伸,可将空间分成九块。其中,上三块/下三块/左三块/右三块中完全包含的矩形个数很好求,为二维偏序。左上/右上/左下/右下中完全包含的矩形个数也可求,为三维偏序。那么将前者总和减去后者总和,显然为不能交此矩形的矩形个数。
那么此题可以转化为求解四个二维偏序,再求解四个三维偏序。CDQ分治可以解决。就是码量可能有亿点点大。
解法二
这种方法很巧妙,用线段树代替普通求解三维偏序的树状数组,可以额外维护两维的信息,使得代码实现仍然是CDQ分治求解三维偏序。
分析矩形 能交 的条件:
其中条件1可以利用排序实现。条件2可以归并时处理。那么剩下三个条件怎么维护呢?
一般的树状数组只能维护一维信息,显然不行。注意到条件4和5构成特殊的形式,即两条“线段”有交。似乎可以用一棵线段树同时维护。再看剩下的条件3。对于同一个 ,满足此条件的 可能有很多;但是此题要求的是能否覆盖,而非有多少覆盖,这使得我们可以进一步利用这棵线段树的“权值”信息维护条件3。容易想到,在线段树上维护 的最大值即可。
总结一下。先排序,满足条件1;归并时分成左右两段,左段按 排,右段按 排,满足条件2。再维护一棵针对左段的线段树,左段中指针每右移一位,线段树中 至 区间用 更新最大值。右段中指针每右移一位,查询线段树中 至 区间内最大值是否大于 。
另外,关于归并完后线段树的清空。显然无法直接覆盖,因为维护的是最大值。那么可以再加一个懒惰标记clear,表示是否清空。pushdown时先下传clear,再下传最大值懒惰标记。
还有一点要注意,线段树中相同的“下标”即判为重合,但此题中横纵坐标的重合不为重合。处理的方式也很简单,即将坐标的开闭做一点小转化。此题中将所有矩形的 与 减1即可。这样的转化很常见。
Code
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int n, ans[N];
int x1[N], x2[N], y1[N], y2[N], mx, my, mpx[N << 1], mpy[N << 1];
struct SegmentTree {int l, r, dat, add, clear;} t[N << 3];
struct Node {int t, xl, xr, yl, yr;} q[N];
int read() {
int x = 0; char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') {x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}
return x;
}
int Matchx(int x) {return lower_bound(mpx + 1, mpx + mx + 1, x) - mpx;}
int Matchy(int x) {return lower_bound(mpy + 1, mpy + my + 1, x) - mpy;}
bool cmp1(const Node &a, const Node &b) {return a.t > b.t;}
bool cmp2(const Node &a, const Node &b) {return a.xl < b.xl;}
bool cmp3(const Node &a, const Node &b) {return a.xr < b.xr;}
void Build(int l, int r, int p) {
t[p].l = l; t[p].r = r;
if (l == r) return ;
int mid = l + r >> 1;
Build(l, mid, p << 1); Build(mid + 1, r, p << 1 | 1);
}
void pushup(int p) {
t[p].dat = max(t[p << 1].dat, t[p << 1 | 1].dat);
}
void pushdown(int p) {
if (t[p].clear) {
t[p << 1].dat = t[p << 1].add = 0;
t[p << 1 | 1].dat = t[p << 1 | 1].add = 0;
t[p << 1].clear = t[p << 1 | 1].clear = 1;
t[p].clear = 0;
}
if (t[p].add) {
t[p << 1].dat = max(t[p << 1].dat, t[p].add); t[p << 1].add = max(t[p << 1].add, t[p].add);
t[p << 1 | 1].dat = max(t[p << 1 | 1].dat, t[p].add); t[p << 1 | 1].add = max(t[p << 1 | 1].add, t[p].add);
t[p].add = 0;
}
}
void Insert(int l, int r, int v, int f, int p) {
if (l <= t[p].l && t[p].r <= r) {
t[p].dat = max(t[p].dat, v); t[p].add = max(t[p].add, v);
if (f) {t[p].dat = t[p].add = 0; t[p].clear = 1;}
return ;
}
pushdown(p);
int mid = t[p].l + t[p].r >> 1;
if (l <= mid) Insert(l, r, v, f, p << 1);
if (r > mid) Insert(l, r, v, f, p << 1 | 1);
pushup(p);
}
int Query(int l, int r, int p) {
if (l <= t[p].l && t[p].r <= r) return t[p].dat;
pushdown(p);
int mid = t[p].l + t[p].r >> 1, res = 0;
if (l <= mid) res = max(res, Query(l, r, p << 1));
if (r > mid) res = max(res, Query(l, r, p << 1 | 1));
pushup(p); return res;
}
void CDQ(int l, int r) {
if (l == r) return ;
int mid = l + r >> 1;
CDQ(l, mid); CDQ(mid + 1, r);
sort(q + l, q + mid + 1, cmp2); sort(q + mid + 1, q + r + 1, cmp3);
int i = l, j = mid + 1;
while (i <= mid && j <= r)
if (q[i].xl <= q[j].xr) Insert(q[i++].yl, q[i].yr, q[i].xr, 0, 1); //i++是为了压行,从右往左执行
else ans[q[j++].t] |= Query(q[j].yl, q[j].yr, 1) >= q[j].xl;
while (j <= r) ans[q[j++].t] |= Query(q[j].yl, q[j].yr, 1) >= q[j].xl;
for (i = l; i <= mid; i++) Insert(q[i].yl, q[i].yr, 0, 1, 1);
}
int main() {
n = read();
for (int i = 1; i <= n; i++) {
mpx[2 * i - 1] = x1[i] = read(); mpy[2 * i - 1] = y1[i] = read();
mpx[2 * i] = x2[i] = read() + x1[i] - 1; mpy[2 * i] = y2[i] = read() + y1[i] - 1;
}
sort(mpx + 1, mpx + 2 * n + 1); mx = unique(mpx + 1, mpx + 2 * n + 1) - (mpx + 1);
sort(mpy + 1, mpy + 2 * n + 1); my = unique(mpy + 1, mpy + 2 * n + 1) - (mpy + 1);
for (int i = 1; i <= n; i++)
q[i] = (Node){i, Matchx(x1[i]), Matchx(x2[i]), Matchy(y1[i]), Matchy(y2[i])};
Build(1, my, 1);
sort(q + 1, q + n + 1, cmp1); CDQ(1, n);
for (int i = 1; i <= n; i++) puts(ans[i] ? "NE" : "DA");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现