离线二维数点算法
本文最后更新于 2025年1月2日 晚上
知识前置
树状数组
一种可以单点修改、区间查询的简便数据结构。结合差分可以实现区间修改、区间查询。详见OI-Wiki。
1 |
|
离散化
一种保留大小关系的映射,$O(V)\rightarrow O(n)$。详见OI-Wiki。
解决问题
平面上有 $n$ 个点,坐标 $(x_i,y_i)$。给出 $m$ 个矩形,左上角 $(x1_j,y1_j)$,右上角 $(x2_j,y2_j)$。询问有多少个点在各个矩阵里,即对于每个 $j$,满足 $x1_j\le x_i\le x2_j\land y1_j\le y_i\le y2_j$ 的 $i$ 的个数。$1\le i\le n$,$1\le j\le m。$
一、朴素算法
适用于 $n$、$m$ 的规模较小的情况,对 $x$ 和 $y$ 的值域无限制。
枚举每个点和每个矩阵,检查点 $i$ 是否在矩阵 $j$ 内。若在,$\text{ans}_j\leftarrow \text{ans}_j+1$。
这样正确性可以保证,因为一个点和对一个矩阵只会做一次贡献。
时间复杂度 $O(nm)$。
二、扫描线
适用于 $x$ 值域无限制、$y$ 值域较小的情况。
例题
洛谷P10814 离线二维数点
给你一个长为 $n$ 的序列 $a$,有 $m$ 次询问,每次询问给定 $l,r,x$,求 $[l,r]$ 区间中小于等于 $x$ 的元素个数。
思路
借助前缀和思想,我们将询问拆成两条:
- $[1,r]$ 的分询问,权值为 $1$;
- $[1,l-1]$ 的分询问,权值为 $-1$。
分询问 $\times$ 权值 的和即为询问的答案。
在平面上用一条线从左向右 (从 $1$ 到 $n$) 扫,每扫到一个点就将它“附着”在直线上。这样,$[1,x]$ 的询问就是直线扫到 $i=x$ 后附着的点在区域 $[1,y]$ 内的答案。
代码实现
实现上,使用 vector<node> c[N]
表示每个横坐标的查询。
使用值域树状数组维护单点修改和区间查询,查询小于等于某个高度的点数量。
时间复杂度 $O(n\log n+km)$,$k=2$。
AC 150.16MB 2.00s
由于用的不多,代码我放到了这里。
三、扫描线 + 离散化
适用于 $x$、$y$ 值域无限制的情况。
洛谷P2163 [SHOI2007] 园丁的烦恼
一上离散化就成蓝题了。
代码实现
使用离散化,将点的规模从 $O(V)$ 降至 $O(n)$。同时,由于插入/查询只需要全序关系,所以正确性可以保证。
将点和询问统一处理,点优先于询问处理。
类前缀和思想,将一个询问拆成四份,以处理一个矩形内的询问。
时间复杂度 $O((n+km)\log(n+km))$,$k=4$。
AC 54.70MB 1.25s
由于用的不多,代码我放到了这里。
注:由于使用了离散化,时间复杂度有所增加,故不能通过 P10814。
四、带权的点
不需要将带权点拆为 $k$ 个点,那样会使时间复杂度大幅增加。
只需要在代码上做一点小改动即可,在树状数组上插入时将权值插入。
代码实现
洛谷P3755 [CQOI2017] 老C的任务
AC 27.35MB 1.06s
1 |
|
机房内一位大佬 $\texttt{wyz}$ 说我写的太长了,我也不知道哪里出了问题导致这么长 QWQ。
其他求解方法
可以使用 CDQ 分治求解三位偏序的方法求解。
还是考虑将原询问拆为四个,点和询问统一处理。然后构建三维偏序关系:
- 第一维:$a_i=x_i$,$a_i\le a_j$;
- 第二维:$b_i=y_i$,$b_i\le b_j$;
- 第三维:$c_i=[i\in\text{Points}]=[i\notin\text{Queries}]$,$c_i<c_j$。
这一维保证查询只能查询到点,而不会查询到别的询问。
对于每个 $j$,满足偏序的 $i$ 的数量即为子询问 $j$ 的答案,再按对应位置权值加起来即可。
由此可见,三位偏序一定可以解二维数点,但二维数点不能解三维偏序。