(未完工)「学习笔记」二维数点问题

news/2025/1/16 13:18:23/文章来源:https://www.cnblogs.com/Z3k7223/p/18674792

0.0 前言

看了一个晚上,加上同桌的讲解,大致了解了二维数点问题的基本思路。

0.1 前置知识

  • 可持久化线段树

  • 树状数组

1.0 概述

二维数点问题的一般形式是形如“给定平面上 \(n\) 个点,每次询问给定一个矩形,求该位于矩形内的点的个数”一类问题,模板题为 P2163 [SHOI2007] 园丁的烦恼 在这里给出两种解法。

2.0 解法

2.0.1 引入

我们先从简化的一维问题开始:如果给定的都是一个数轴上的点,询问的都是某个区间内点的个数该怎么办?一个比较 trivial 的想法是将坐标离散化后扔进权值线段树内,询问的时候直接查询即可。

2.0.2 解法一:可持久化线段树

这里分享一种此题题解讲述较少的,不使用树状数组等其它数据结构的可持久化线段树的解法。

当问题扩展到二维之后,发现对于每一组纵坐标相同的点都建立一棵权值线段树似乎有些不太可行,可不可以用一棵线段树就维护所有点的信息呢?自然的,可持久化线段树就成为了首选,我们依旧先分别离散化横坐标和纵坐标,考虑先按其中一维(这里选择 \(x\) 这维)排序后一组一组地同时插入 \(x\) 相同的点至线段树中,并将另一维作为权值(这里选择 \(y\) 这维)。如果觉得还是很抽象的话可以看一个实例:

假设给定的是这些点(坐标已全部离散化,点的编号即为插入线段树的顺序):

第一组,我们把 \(A,B\) 两点插入:

接着,继续向右走,插入 \(C\) 点:

依此类推,每次向右走,直到最后一个点被插入。

查询的时候,我们根据输入的坐标,找出离散化后对应的坐标。由于我们记下了整块插入时的根结点,因此我们只需要把这两个根节点的横坐标编号和对应的纵坐标扔进线段树查询即可。

解法一 Code

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int n, q;
int nx, ny;struct node {int x, y;
} a[N];
int tmpx[N], tmpy[N];
vector<int>t[N];
int rt[N];
int ls[N << 5], rs[N << 5], tree[N << 5];
int cnt;void build(int &p, int l, int r) {p = ++cnt;if (l == r) {return;}int mid = (l + r) >> 1;build(ls[p], l, mid);build(rs[p], mid + 1, r);
}int modify(int p, int l, int r, int k) {int x = ++cnt;ls[x] = ls[p], rs[x] = rs[p];tree[x] = tree[p] + 1;if (l == r) {return x;}int mid = (l + r) >> 1;if (k <= mid) {ls[x] = modify(ls[x], l, mid, k);} else {rs[x] = modify(rs[x], mid + 1, r, k);}return x;
}int query(int p1, int p2, int l, int r, int L, int R) {if (L <= l && r <= R) {return tree[p2] - tree[p1];}int mid = (l + r) >> 1;int res = 0;if (L <= mid) {res += query(ls[p1], ls[p2], l, mid, L, R);}if (R > mid) {res += query(rs[p1], rs[p2], mid + 1, r, L, R);}return res;
}int find_upx(int x) {int l = 1, r = nx, res = 0;while (l <= r) {int mid = (l + r) >> 1;if (x <= tmpx[mid]) {res = mid;r = mid - 1;} else {l = mid + 1;}}return res;
}int find_upy(int x) {int l = 1, r = ny, res = 0;while (l <= r) {int mid = (l + r) >> 1;if (x <= tmpy[mid]) {res = mid;r = mid - 1;} else {l = mid + 1;}}return res;
}int find_lowx(int x) {int l = 1, r = nx, res = 0;while (l <= r) {int mid = (l + r) >> 1;if (tmpx[mid] <= x) {res = mid;l = mid + 1;} else {r = mid - 1;}}return res;
}int find_lowy(int x) {int l = 1, r = ny, res = 0;while (l <= r) {int mid = (l + r) >> 1;if (tmpy[mid] <= x) {res = mid;l = mid + 1;} else {r = mid - 1;}}return res;
}int main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> q;if (!n) {while (q--) {cout << "0\n";}return 0;}for (int i = 1; i <= n; i++) {cin >> a[i].x >> a[i].y;tmpx[i] = a[i].x, tmpy[i] = a[i].y;}sort(tmpx + 1, tmpx + 1 + n), sort(tmpy + 1, tmpy + 1 + n);nx = unique(tmpx + 1, tmpx + 1 + n) - tmpx - 1;ny = unique(tmpy + 1, tmpy + 1 + n) - tmpy - 1;build(rt[0], 1, ny);for (int i = 1; i <= n; i++) {a[i].x = lower_bound(tmpx + 1, tmpx + 1 + nx, a[i].x) - tmpx;a[i].y = lower_bound(tmpy + 1, tmpy + 1 + ny, a[i].y) - tmpy;t[a[i].x].push_back(a[i].y);}for (int i = 1; i <= nx; i++) {rt[i] = rt[i - 1];for (auto j : t[i]) {rt[i] = modify(rt[i], 1, ny, j);}}while (q--) {int x1, y1, x2, y2;cin >> x1 >> y1 >> x2 >> y2;x1 = find_upx(x1), y1 = find_upy(y1);x2 = find_lowx(x2), y2 = find_lowy(y2);cout << query(rt[x1 - 1], rt[x2], 1, ny, y1, y2) << '\n';}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/870087.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS扩容boot分区并升级内核

本文作者CVE-柠檬i:https://www.cnblogs.com/CVE-Lemon 前言 由于安装k8s需要升级内核,但我自己的的boot分区只有200M大小,无法安装新内核,所以干脆把swap分区分给boot了。在此期间关于grub的操作踩了好多坑,所以特此记录一下正确操作。 使用rpm安装新内核,下载链接:htt…

陨石的秘密

题目链接: https://www.acwing.com/problem/content/319/ 题目描述提取题目大意: 构造 L1对{},L2对[],L3对() 组成的深度为D的括号序列,求方案数。 并且中括号里不能有大括号,小括号里不能有中括号和大括号。 思路:考虑“第一段”括号序列(它作为一个整体,只能是{} []…

Xorto

给定一个长度为n的整数数组,问有多少对互不重叠的非空区间,使得两个区间内的数的异或和为0。暴力,每次找一个中点,找左右两边异或值一样的区间 #include<bits/stdc++.h> #define int long long #define TEST #define TESTS int _; cin >> _; while(_--) using…

【OAuth2框架】理解和实战 OAuth2 认证授权

你知道互联网大厂最怕的是什么吗?但凡有点这样的风吹草动,我们就要花费大量的时间进行修复和上线。一点都不敢耽误,对于紧急类型的,基本当天发现,当天就要升级上线。那是什么问题呢?🤔 其实最怕的就是各类组件漏洞! 有这么一个东西,13scan - 安全漏洞扫描 它可以扫描…

2025.1.15——1200

2025.1.15——1200Q1. 1200 简单来说就是给定3个数组,每个数组选择一个数,三者下标不同,问三者和的最大值。 Winter holidays are coming up. They are going to last for \(n\) days. During the holidays, Monocarp wants to try all of these activities exactly once wi…

【附源码】JAVA大学生竞赛管理系统源码+SpringBoot+VUE+前后端分离

学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:大学生竞赛管理系统 。 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 系统测试截图系统视频演示https://githubs.xyz/show/343.mp4二. 系统概述【 系…

Ubuntu升级Linux内核教程

本文作者CVE-柠檬i:https://www.cnblogs.com/CVE-Lemon 本文使用的方法是dpkg安装,目前版本为5.4.0-204,要升级成5.8.5版本下载 下载网站:https://kernel.ubuntu.com/mainline/ 在该网站下载deb包,选择自己想要升级的版本,这里是5.8.5https://kernel.ubuntu.com/mainline/…

psSign、random推导

入口:传递的参数值:sign函数 function() {var _0x36c5d3 = _0x79c1ce;let _0x479298 = arguments[_0x36c5d3(0x5a9, PHXL)] > 0x0 && void 0x0 !== arguments[0x0] ? arguments[0x0] : {};try {var _0x1a6f66;if (!_0x32d6a8[_0x36c5d3(0x4d7, l!Uo)][_0x36c5d3(…

渗透测试中如何反编译JAR

反编译是渗透测试中的重要环节之一。 通过反编译,我们可以得到程序的项目结构、相关资源以及配置的数据库等信息。 本文以常见SpringBoot项目为例,对其进行反编译。 Spring Boot 是一个基于 Spring 的框架,旨在简化 Spring 应用的配置和开发过程,通过自动配置和约定大于配置…

专项训练2

贪心专题 1. [NOIP2015 普及组] 推销员 link:https://www.luogu.com.cn/problem/P2672 思路跟正解大差不差,但想的有点复杂了。先把所有的按疲劳值排个序,(这样省却了找最大疲劳值的过程),然后只用考虑第x大的和后面距离+疲劳值最大值的比较即可(累了,不想写了) 2. Tw…

vue2子组件获取父组件的实例以及数据,vue2子组件获取父组件的数据

多个组件引入同一个js文件,实例化对象,数据不会错乱,再引入相同的组件,例如每个页面都需要引入到一个分页组件,然后分页组件需要获取各自父组件中的实例对象 通过 this.$parent 即可获取到父组件中的数据 所以在使用子组件时可以不用在组件上传入数据 公共js文件functi…