二维数点 / 二维偏序
介绍
考虑如下问题:
- 给出一个二维平面內的若干个点,多次询问某个矩形区域內包含多少个点(包括边界)。
- 给一个长为 \(n\) 的序列,多次询问区间 \(\left[l,r \right]\) 中值在 \(\left[x,y\right]\) 内的元素个数。
上述是二维数点问题。这是是指求满足 \(x_i>x_j,y_i>y_j\) 的二元组 \((i,j)\) 数量的题目,它属于二维偏序的一种。二维偏序会具有不同的偏序关系,形式上,常常是在一个数列上,对于两个点,如果满足一些条件则计数的一类题目。
这类题目可以使用树状数组来解决。一些条件枚举 \(1\sim n\),另一些条件用树状数组维护 ,多重条件可能需要二维树状数组。
引例
P1908 逆序对
该题目为一维偏序,即求 \(i<j,a_i>a_j\) 的数对个数。
\(i=1\sim n\) 枚举每一个数,我们需要统计在 \(a_i\) 前面的比它大的数有多少个。如果在每次枚举到 \(a_i\) 时把它作为下标插入树状数组,那么在此之前树状数组里只存了在它前面的数,于是 \(query(a_i)\) (查询 \(1\sim i\) 的和)就可以得到在 \(a_i\) 前面的小于等于它的数有多少个。前面的数一共有 \(i-1\) 个,那么去掉小于等于 \(a_i\) 的数的数量就是比 \(a_i\) 大的数的数量。每一轮处理完后再将 \(a_i\) 插入树状数组。由于\(a_i\) 值域可能很大,所以用它作为树状数组下标得先进行离散化。
模板
P10814 【模板】离线二维数点
介绍中提到的两类问题其实可以互相转化。对于一个数列,将元素的值作为第二维(\(y\) 轴),这样在坐标系里就变成在矩形内处理。
假设我们将矩形按横坐标排序,对于当前 \(x\),\(x' \le x\) 的点的纵坐标都已知,那么直接查询 \(query(y)\) 就得到了 \(x\) 前的小于等于 \(y\) 的数的数量。将此时的 \(x\) 视为 \(r\),那么再用一样的方法在之前求出 \(l-1\) 前的小于等于 \(y\) 的数的数量,相减就是答案。为了这样做,我们需要把每个询问的 \(l,r\) 拆成两个询问,\(x=l-1\) 时 \(ans\) 减去值,\(x=r\) 时 \(ans\) 加上值。用若干个 \(vector\) 存储每个 \(x\) 时的询问数量,遍历 \(x\),向树状数组存入当下 \(a_i\) 后就处理所有该 \(vector\) 的所有询问,把贡献加至原询问处。这样做就保证了处理每个询问时只有在它的 \(x\) 前面的被加入到树状数组中。因此该算法是离线的。
更一般地,如果这道题把求值小于等于 \(x\) 改成求值在 \(x,y\) 区间内,根据二维前缀和,我们把每个询问拆成四块,算答案时进行加减,其他就一样了。
实现时有几点要注意,一是如果值域小不进行离散化,那么树状数组下标的最大值是 \(i\in n,max(a_i)\) 而非 \(n\),因此 \(update\) 时最大值也是。二是如果坐标可能取到 \(0\) 记得统一加一,因为树状数组处理 \(0\) 下标会死循环。
一般题目中的偏序关系不会像数点这么明显,可能需要自己去寻找。
例题
P2163 [SHOI2007] 园丁的烦恼
\(X_{max},Y_{max}\) 在坐标和询问里都要找。
P3755 [CQOI2017] 老C的任务
\(x,y\) 坐标分别离散化。
树状数组存 \(y\) 坐标,所以下标最大值是离散化后不同 \(y\) 坐标的数量(即 \(y\) 离散化后的 \(len\));而根据 \(x\) 坐标一个一个加入值时,应循环到离散化后不同 \(x\) 坐标的数量。之前离散化后上述值都可以是 \(n\) 是因为只对数列离散化,就直接将 \(n\) 赋值为新的 \(len\)。
P1972 [SDOI2009] HH 的项链
要查询一段区间中不同元素的数目,那么对于某一些相同的元素,我们可以只关注它的某一个位置,因为这些元素只能产生 \(1\) 的贡献,但是这个位置的选择又得使得所有和该元素有关的询问都包含这个位置。
对于若干个询问的区间 \(\left[l,r\right]\),如果他们的 \(r\) 都相等的话,那么项链中出现的同一个数字,一定是只关心出现在最右边的那一个的,这样在该元素会产生贡献的区间中一定会有贡献产生,同时不会被后面出现的相同元素影响。因此离线处理,以区间的 \(r\) 排序,依次从左向右地处理和修改整个数组,这样才能保证我们对于前一个区间的修改不会影响到后面区间的询问。
树状数组以位置作为下标,询问时直接输出区间和。
在树状数组中,如果要删除某个点,就给这个点 \(update\) 一个自己的相反数。
CF1320C World of Darkraft: Battle for Azathoth
线段树维护。
接下来是一类树上二维数点问题,一般转化的方法,是将树上的结点放在平面直角坐标系里,每个结点对应坐标系中的一个点。将横坐标设成结点的 \(dfs\) 序,纵坐标依题目而定,可能是设成每个结点的深度,这样遇到要求子树内且限定深度的信息,就可以转化成坐标系里求矩形的问题。
P8844 [传智杯 #4 初赛] 小卡与落叶
转化为问一个子树内有多少个结点深度大于等于 \(x\)。
P3899 [湖南集训] 更为厉害
对于 \(b\) 在 \(p\) 子树内的情况,想出每个 \(b\) 点贡献为 \(size_b-1\) 后,就可以转化为问一个子树内有多少个结点深度在 \(dep_p+1\sim dep_p+k\) 内。

浙公网安备 33010602011771号