首页 > 其他分享 >(未完工)「学习笔记」二维数点问题

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

时间:2025-01-16 13:20:56浏览次数:1  
标签:int 线段 数点 mid 笔记 二维 tmpy tmpx find

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;
}

标签:int,线段,数点,mid,笔记,二维,tmpy,tmpx,find
From: https://www.cnblogs.com/Z3k7223/p/18674792

相关文章

  • Windows系统下NoteFlow的下载:提供直观、易用的界面,使用户能够轻松创建和连接笔记节点
    NoteFlow(适用于python3.9及以上):功能:节点笔记软件,有助于更好地组织和管理笔记内容。特点:提供直观、易用的界面,使用户能够轻松创建和连接笔记节点。一.从github上获取创作者的代码跳伞到github下载文件压缩包二.Windows只按照pip就行使用pip安装(适用于所有平台)打开命令行......
  • 关于2024秋季学期期末笔记系列
    碎碎念该系列笔记既不够详细,也不够精简,仅作为我期末挣扎的一个存档,不建议各位花时间阅读传送门排序按期末考试先后顺序人工智能导论复变函数光学概率论与数理统计理论力学......
  • Linux C 使用ZBar库解析二维码和条形码
    1.编译zbar库下载zbar库源码,这里需要注意下,如果识别的二维码中有中文的话,会出现乱码,一般二维码里中文为UTF-8编码,zbar会默认给你把UTF-8转换为ISO8859-1。有两种解决办法,一是自己再转换一下编码格式;二是修改下zbar源码,很简单,只需要修改源码目录下的zbar/qrcode/qrdectxt.c......
  • 《构建之法》读书笔记
    《构建之法》读书笔记:探索成长与实践的智慧阅读《构建之法》,是一次对软件开发领域深度探索的旅程,更是一场对自我认知与工作方法的革新。这本书为我打开了一扇洞察高效工作与个人成长的大门,书中诸多理念让我深感共鸣,并在实际生活与工作中不断思索、践行。书中提及的“个人开发流......
  • 《CPython Internals》阅读笔记:p152-p176
    《CPythonInternals》学习第10天,p152-p176总结,总计25页。一、技术总结1.addinganitemtoalistmy_list=[]my_list.append(obj)上面的代码涉及两个指令:LOAD_FAST,LIST_APPEND。整章看下来这有这点算是可以记的了,其它的只感觉作者在零零碎碎的罗列内容。二、英语......
  • Golang学习笔记_24——泛型
    Golang学习笔记_21——ReaderGolang学习笔记_22——Reader示例Golang学习笔记_23——error补充文章目录泛型1.泛型中的类型参数1.1类型参数声明1.2类型参数的约束1.3类型参数的实例化2.泛型函数3.泛型类型4.泛型接口源码泛型Go语言从1.18版本开始引入......
  • JavaWeb课后笔记及体会分享(每日一更)
           从今天开始学习JavaWeb,在接下来的时间里我将学习JavaSE,MySQL,Web前端,JavaEE,SSM三大框架,SpainBoot,SpringCloud,以及一些常见面试题的练习。1.IDEA常用快捷键  Shift两次:包含各种文件、方法的搜索Ctrl+Shift+F:根据输入内容查找整个项目或指定目录内文件......
  • 后缀自动机 (SAM) 学习笔记
    \(\text{后缀自动机(SAM)学习笔记}\)一、定义字符串\(s\)的SAM是一个接受\(s\)的所有后缀的最小DFA(确定性有限自动机或确定性有限状态自动机),也就是说:SAM是一张有向无环图。它的结点是图中的状态,边是状态之间的转移。SAM有源点\(t_0\),且其它各结点均可从\(t_0......
  • THREE.js学习笔记6——Geometries
    这一小节学习THREE.js中的物理模型。什么是geometry?(英文解释,翻译为中文就看不懂了,直接看英语吧)Composedofvertices(pointcoordinatesin3Dspaces)andfaces(trianglesthatjointhoseverticestocreateasurface)Canbeusedformeshesbutalsoforparticles......
  • 线段树学习笔记
    什么是线段树线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息统计,比树状数组更为通用、直观,支持单点修改、区间修改、区间查询。线段树维护的数据具有可并性,比如区间和、区间积、区间最值等等。模板建树voidbuild(intl,intr,intp){ tre[p].l=l;tre[p].r=r;......