[SHOI2007] 园丁的烦恼

[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,m10
  • 对于 100% 的数据,保证 0n5×1051m5×1050x,y,a,b,c,d107acbd

 

解题思路

  二维数点问题,就是平面上有若干个点,每次询问某个矩形内有多少个点。

  由二维前缀和知道,定义 Sx,y 表示以 (x,y) 为右上角,左下角为原点 (1,1) 的矩形中点的数量。如果矩形的左下角为 (x1,y1) 右上角为 (x2,y2),那么该矩形中含有的点的数量就是Sx2,y2Sx2,y11+Sx11,y2+Sx11,y11

  而由于值域很大,因此我们不可能通过平方级别的复杂度预处理出来前缀和 S,考虑优化。在前缀和的计算公式中,当询问某个矩形中点的数量时,本质上是拆分成 4 个左下角均为 (1,1),右上角分别为 (x2,y2)(x2,y11)(x11,y2)(x11,y11) 的矩形计算得到。只考虑这些左下角为 (1,1) 的矩形,如果点 (x,y) 在矩形内,那么就要同时满足 {1xx1yy

  考虑固定 x 这个约束条件,对这些左下角为 (1,1) 的矩形以 x 为关键字从小到大排序,枚举矩形的同时用树状数组维护点的数量。当枚举到矩形 (xi,yi) 时,把所有满足 xxi 的点在 y 上加 1,并用树状数组来动态维护前缀和(所有横坐标不超过 x,纵坐标不超过某个值的点的数量)。那么矩形 (xi,yi) 内点的数量就是 query(yi)

  所以做法就是考虑离线处理,把 m 个矩形分别拆分成上述说到的 4 个矩形,用四元组 (xi,yi,c,idx) 来表示,其中 (xi,yi) 表示矩形的右上角,c 表示该矩形在前缀和公式中的系数(11),idx 表示来自第几个矩形,那么就会得到 4m 条记录。按照第一个参数 x 从小到大排序,枚举的过程中用树状数组维护横坐标小于 xi 的点的数量,然后把结果 c * query(yi) 累加到第 idx 个矩形的结果中。

  一些注意事项,由于坐标可能为 0,为了方便这里把所有坐标都加 1。同时由于坐标值域 A107,内存限制有 125 MB,所以不需要离散化。

  AC 代码如下,时间复杂度为 O(mlogm+(n+m)logA)

#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

posted @   onlyblues  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示