[SHOI2007] 园丁的烦恼
题目背景
很久很久以前,在遥远的大陆上有一个美丽的国家。统治着这个美丽国家的国王是一个园艺爱好者,在他的皇家花园里种植着各种奇花异草。
有一天国王漫步在花园里,若有所思,他问一个园丁道: “最近我在思索一个问题,如果我们把花坛摆成六个六角形,那么……”
“那么本质上它是一个深度优先搜索,陛下。”园丁深深地向国王鞠了一躬。
“嗯……我听说有一种怪物叫九头蛇,它非常贪吃苹果树……”
“是的,显然这是一道经典的动态规划题,早在 N 元 $4002$ 年我们就已经发现了其中的奥秘了,陛下。”
“该死的,你究竟是什么来头?”
“陛下息怒,干我们的这行经常莫名其妙地被问到和 OI 有关的题目,我也是为了预防万一啊!” 王者的尊严受到了伤害,这是不可容忍的。
题目描述
看来一般的难题是难不倒这位园丁的,国王最后打算用车轮战来消耗他的实力: “年轻人,在我的花园里有 $n$ 棵树,每一棵树可以用一个整数坐标来表示,一会儿,我的 $m$ 个骑士们会来轮番询问你某一个矩阵内有多少树,如果你不能立即答对,你就准备走人吧!”说完,国王气呼呼地先走了。
这下轮到园丁傻眼了,他没有准备过这样的问题。所幸的是,作为“全国园丁保护联盟”的会长——你,可以成为他的最后一根救命稻草。
输入格式
第一行有两个整数 $n, m$,分别表示树木个数和询问次数。
接下来 $n$ 行,每行两个整数 $x, y$,表示存在一棵坐标为 $(x, y)$ 的树。有可能存在两棵树位于同一坐标。
接下来 $m$ 行,每行四个整数 $a, b, c, d$,表示查询以 $(a, b)$ 为左下角,$(c, d)$ 为右上角的矩形内部(包括边界)有多少棵树。
输出格式
对于每个查询,输出一行一个整数表示答案。
样例 #1
样例输入 #1
3 1
0 0
0 1
1 0
0 0 1 1
样例输出 #1
3
提示
数据规模与约定
- 对于 $30\%$ 的数据,保证 $n, m \leq 10$。
- 对于 $100\%$ 的数据,保证 $0 \leq n \leq 5 \times 10^5$,$1 \leq m \leq 5 \times 10^5$,$0 \leq x, y, a, b, c, d \leq 10^7$,$a \leq c$,$b \leq d$。
解题思路
二维数点问题,就是平面上有若干个点,每次询问某个矩形内有多少个点。
由二维前缀和知道,定义 $S_{x,y}$ 表示以 $(x,y)$ 为右上角,左下角为原点 $(1,1)$ 的矩形中点的数量。如果矩形的左下角为 $(x_1,y_1)$ 右上角为 $(x_2,y_2)$,那么该矩形中含有的点的数量就是$$S_{x_2 \, , \, y_2} \; - \; S_{x_2 \, , \, y_1-1} \; + \; S_{x_1-1 \, , \, y_2} \; + \; S_{x_1-1 \, , \, y_1-1}$$
而由于值域很大,因此我们不可能通过平方级别的复杂度预处理出来前缀和 $S$,考虑优化。在前缀和的计算公式中,当询问某个矩形中点的数量时,本质上是拆分成 $4$ 个左下角均为 $(1,1)$,右上角分别为 $(x_2,y_2)$、$(x_2,y_1-1)$、$(x_1-1,y_2)$、$(x_1-1,y_1-1)$ 的矩形计算得到。只考虑这些左下角为 $(1,1)$ 的矩形,如果点 $(x',y')$ 在矩形内,那么就要同时满足 $\displaylines{\begin{cases} 1 \leq x' \leq x \\ 1 \leq y' \leq y \end{cases}}$。
考虑固定 $x$ 这个约束条件,对这些左下角为 $(1,1)$ 的矩形以 $x$ 为关键字从小到大排序,枚举矩形的同时用树状数组维护点的数量。当枚举到矩形 $(x_i,y_i)$ 时,把所有满足 $x' \leq x_i$ 的点在 $y'$ 上加 $1$,并用树状数组来动态维护前缀和(所有横坐标不超过 $x'$,纵坐标不超过某个值的点的数量)。那么矩形 $(x_i,y_i)$ 内点的数量就是 query(yi)
。
所以做法就是考虑离线处理,把 $m$ 个矩形分别拆分成上述说到的 $4$ 个矩形,用四元组 $(x_i,y_i,c,\text{idx})$ 来表示,其中 $(x_i,y_i)$ 表示矩形的右上角,$c$ 表示该矩形在前缀和公式中的系数($1$ 或 $-1$),$\text{idx}$ 表示来自第几个矩形,那么就会得到 $4m$ 条记录。按照第一个参数 $x$ 从小到大排序,枚举的过程中用树状数组维护横坐标小于 $x_i$ 的点的数量,然后把结果 c * query(yi)
累加到第 $\text{idx}$ 个矩形的结果中。
一些注意事项,由于坐标可能为 $0$,为了方便这里把所有坐标都加 $1$。同时由于值域 $A \leq 10^7$,内存限制有 $125 \text{ MB}$,所以不需要离散化。
AC 代码如下,时间复杂度为 $O(n \log{A})$:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 5e5 + 10, M = 1e7 + 10;
PII p[N];
struct Node {
int x, y, c, idx;
}q[N * 4];
int tr[M];
int ans[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int c) {
for (int i = x; i < M; i += lowbit(i)) {
tr[i] += c;
}
}
int query(int x) {
int ret = 0;
for (int i = x; i; i -= lowbit(i)) {
ret += tr[i];
}
return ret;
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d %d", &p[i].first, &p[i].second);
p[i].first++, p[i].second++;
}
for (int i = 0, j = 0; i < m; i++) {
int x1, y1, x2, y2;
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
x1++, y1++, x2++, y2++;
q[j++] = {x2, y2, 1, i};
q[j++] = {x1 - 1, y1 - 1, 1, i};
q[j++] = {x2, y1 - 1, -1, i};
q[j++] = {x1 - 1, y2, -1, i};
}
sort(p, p + n);
sort(q, q + 4 * m, [&](Node &a, Node &b) {
return a.x < b.x;
});
for (int i = 0, j = 0; i < m << 2; i++) {
while (j < n && p[j].first <= q[i].x) {
add(p[j++].second, 1);
}
ans[q[i].idx] += q[i].c * query(q[i].y);
}
for (int i = 0; i < m; i++) {
printf("%d\n", ans[i]);
}
return 0;
}
参考资料
【数据结构】二维数点/二维偏序:https://blog.csdn.net/qq_30320171/article/details/129787418
标签:10,园丁,int,烦恼,++,leq,SHOI2007,矩形 From: https://www.cnblogs.com/onlyblues/p/17842256.html