二维数点进阶
二维数点进阶
基本类型
根据有无更改,是否在线,以及聚合运算性质,可以分出来很多种
接下来用 \([(x_1,y_1),(x_2,y_2)]\) 表示左上角为 \((x_1, y_1)\),右下角为 \((x_2,y_2)\)(我习惯放到第四象限讨论
1. 静态,离线,存在逆元
经典问题,由于离线,可以按照某一维排完序后增添相应的扫描线,然后将线上的信息记录,这里推荐使用树状数组,常数小
查询 \([(x_1,y_1),(x_2,y_2)]\) 可以转成查询 \([(0,0),(x_2,y_2)]\),\([(0,0),(x_1-1,y_2)]\),\([(0,0),(x_2,y_1-1)]\),\([(0,0),(x_1-1,y_1-1)]\) 这四个从原点出发的查询
2. 静态,在线,存在逆元
要求在线,我们就可以使用可持久化线段树记录历史状态,查询依然不变
3. 矩形操作 + 单点查询
考虑静态,离/在线,存在逆元
还是一样的,只不过反了过来,对于矩阵操作,根据二维差分的操作,依然可以拆成四个从 \((0,0)\) 出发的矩形区域
单点就很简单了
4. 静态,离/在线,不存在逆元
这个时候树状数组就是不行的了,除非查询 \(x_i\le X,y_i\le Y\) 这般的
-
对于 \(x_i\le X, l_i \le y_i \le r_i\) 这种可以用线段树, \(\mathcal O(\log n)\)
-
对于更一般的 \(x_i \in[L_i, R_i], y\in [l_i,r_i]\) 这种,线段树和树状数组就解决不了了,要用到树套树等高级方法,\(\mathcal O(\log^2 n)\)
一般是不会这么考的,出现这种情况,很大可能是建模建错了
5. 动态,离线,存在逆元
类似于线段树分治,我们将每一个点的出现起止时间都记录下来,然后就会变成静态的三维数点问题,并非我们讨论范畴,所以不惜将
6. 矩形操作,矩形查询
二维差分,考虑树状数组是如何做到区修区查的
定义 \(d_{ij}\) 为 \(a_{ij}\) 的差分数组,所以 \(a_{xy} = \sum\limits_{i = 1}^{x}\sum\limits_{j=1}^{y}\)
所以前缀和就可以表示成:
观察最后的式子,不难发现,我们需要维护 \(d_{ij}\),$ id_{ij}\(,\)jd_{ij}\(,\)ijd_{ij}$
但是这里千万不可以使用树状数组,光是开一个二维的本地栈空间就爆了
7. 斜线
这种没学,鸽着
实践 & 建模技巧
二维数点的版子你是会了,可是什么时候用呢?
例题
BZOJ 2874 训练士兵
就是类型六,要用可持久化线段树,并且注意什么时候改用离散化后的值,什么时候用原值
AT_abc233_h [ABC233Ex] Manhattan Christmas Tree
容易发现直接做第 \(k\) 大的话十分困难,所以我们可以思考如何不做第 \(k\) 大
我们想一想第 \(k\) 大怎么出现的,那就是距离小于它的恰好有 \(k- 1\) 个,诶,这个恰好就限定了范围,可是我们并不知道真正的第 \(k\) 大的距离
不知道是可以猜的,就像二分答案,可以二分出来一个距离,然后看这个距离划分出区域内点有多少
但是,由于是曼哈顿距离,到某个点的距离为定值的点构成了一个正方形,还是旋转了 \(45^\circ\) 的正方形
既然旋转了,那就转回来,对于点 \((x,y)\),旋转 \(45^\circ\) 后就变成了 \((x+y,x-y)\) (这里曼哈顿距离在旋转后就变成了切比雪夫距离,即 \(\max(|x+y|,|x-y|) = |x|+|y|\)
对应的,由 \((x,y)\) 变成 \(\left(\frac{x+y}{2}, \frac{x-y}{2}\right)\),就可以从切比雪夫距离变成曼哈顿距离,即 \(\max(|x|,|y|) = \frac{|x+y|+|x-y|}{2}\)
这样子,这个正方型就摆正了,就可以进行二维数点了
View Code
bool check(int distance, int k, int x, int y) {
int x1 = x - distance, x2 = x + distance;
int y1 = y - distance, y2 = y + distance;
if (x1 < 0) x1 = 0;
if (x2 > 200000) x2 = 200000;
if (y1 < 1) y1 = 1;
if (y2 > 400000) y2 = 400000;
if (x1 > x2 || y1 > y2) return false;
int cnt = query(root[x2], 1, 400000, y1, y2) -
(x1 > 0 ? query(root[x1 - 1], 1, 400000, y1, y2) : 0);
return cnt >= k;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
int x, y;
cin >> x >> y;
p[i].x = x + y;
p[i].y = x - y + 200001;
}
sort(p + 1, p + n + 1);
root[0] = 0;
int j = 1;
for (int i = 0; i <= 200000; i++) {
if (i > 0) root[i] = root[i - 1];
else root[i] = 0;
while (j <= n && p[j].x == i) {
root[i] = update(root[i], 1, 400000, p[j].y, 1);
j++;
}
}
cin >> q;
for (int i = 1; i <= q; i++) {
int a, b, k;
cin >> a >> b >> k;
int x = a + b;
int y = a - b + 200001;
int l = 0, r = 200000;
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid, k, x, y)) r = mid;
else l = mid + 1;
}
cout << l << "\n";
}
}
P10281 [USACO24OPEN] Grass Segments G
分类讨论
首先要使得覆盖的长度超过 \(k_i\),这一个区间的长度也就必须超过 \(k_i\)
我们可以先将条件都写出来:
- \(l_j \le r_i - k_i\)
- \(r_j \ge l_i+k_i\)
这两个条件不可以同时满足
先考虑 \(l_j\le r_i - k_i\),这个时候 \(r_j \ge r_i\)
考虑 \(r_j \ge l_i+k_i\),防止数重,再加上一个条件 \(r_j \lt r_i\),这个时候 \(r_j - l_j \ge k_i\)
这个时候就转化成了两个二维数点,一个对于二元组 \((l,r)\),另一个针对 \((r,r-l)\)
涉及两个二维数点,实现稍有些复杂
最后需要注意一点,对于 \((l,r)\) 的二维数点,有可能会将 \((l_i,r_i)\) 给算进去,最后要减掉
P13094 [FJCPC 2025] 帕累托前沿
直接暴力求是肯定过不了的
观察题目给出的条件,就可以发现,满足该条件的数肯定是该区间内的最大值,并且唯一
所以我们稍微改一下最大的定义:\(a = \max S \iff \nexists k \in S \setminus \{a\},k\ge a\)
这个时候,我们发现,由于相同的值会阻碍彼此成为最大值,所以唯一的问题被解决了
定义 \(R_i\) 表示是的 \(x_i\) 能成为最大值的最大区间,\(l_i\) 是其左边界,\(r_i\) 是右边界(这里拿 \(x\) 举例,\(y\) 同理
这个时候,问题转化成了求使得 \(l_i \le ql_i \le i\le qr_i \le r_i\) 的个数,这就变成了二维数点
但是 \(R_i\) 怎么求出来呢?
构建一个区间最大值的线段树,在上面做线段树二分,找到在 \(i\) 前面最后一个 \(\ge x_i\) 以及在 \(i\) 后面第一个 \(\ge x_i\) 的位置
P11364 [NOIP2024] 树上查询
我何德何能居然能做noipT4了,Σ(⊙▽⊙"a
这就是前面两道题的融合版,只不过有一个性质比较难推而已
先把这个性质写出来,等会儿证明:
题目要求 \(\max\limits_{l\le l'\le r'\le r \land r'-l'+1\ge k}\text{dep}_ {\text{lca}^*(l', r')}\)
即所有/子区间 LCA 的深度最大值
看到这个 \(\min\) 再结合上一道题,我们就会发现完全可以沿用上一题的方法,预处理 \(R_i\)
这个时候就多出了许多 \(R\),题目又要求子区间的长度 \(\ge k\),也就是这些 \(R\) 与 \((l,r)\) 的交的长度必须大于等于 \(k\),诶,这不就是上上道题吗?
所以这么就做完了
Proof
这里用 \(E\) 表示 Euler 序,\(D\) 表示对应的深度,用 \(f\) 表示第一次进入的时间戳
首先,依据 RMQ 求 LCA,可以知道:
然后考虑将 \(\operatorname{lca}^*\) 改成我们熟悉的样子:假设 \(a\) 是 \([l,r]\) 中 \(f\) 最小的,\(b\) 则是最大的
那么
为方便,不妨设 \(a = l\),\(b = r\)
所以,转化为证明:
将区间 \([f[l],f[r]]\) 按照相邻点分割
所以
而
带入即得

浙公网安备 33010602011771号