二维数点
有时,我们要处理的操作有两个维度的约束条件。例如,有 \(n\) 个元素,每个元素都有两个属性 \(a_i\) 和 \(b_i\),要求统计所有满足 \(a_i\in[l_1,r_1]\) 且 \(b_i\in [l_2,r_2]\) 的元素的贡献。这种问题被称为二维数点问题。
通常情况下,二维数点问题可以直接离线解决;若强制在线,则可以使用[主席树]解决。
整体思路
我们将所有操作(修改+查询)混合并按照其中一个维度排序,按顺序依次处理(这个过程叫做扫描线),第二个维度用数据结构维护并统计贡献。
使用二维数点的条件
如何判断约束是几个维度的,能否直接离线后二维数点解决?
在不等式形式(\(x\le r_0\) 或 \(l_0\le x\le r_0\) 的形式)的约束中,同一个不等号两侧的两个式子可以看作是在同一个维度。或:几个不等式就是几个维度。
- 只包含一个维度的约束条件可以直接 在线+一维数据结构 解决;
- 包含两个维度的约束条件通常 可以使用二维数点解决。
注意特殊情况:
- 若维护的信息不可差分(最值、gcd等),且两个维度的约束同时具有上下界(\(l_1\le x\le r_1\),\(l_2\le y\le r_2\)),则无法通过数点转化问题(主席树也做不了,只能树套树 ——fjj)。
- 若维护的信息不可差分,但只有一个维度的约束同时具有上下界(\(x\le r_1\),\(l_2\le y\le r_2\)),可以对另一个维度(\(x\))排序并扫描线,该维度(\(y\))用线段树解决(因为线段树不要求维护的信息可差分)
- 若维护的信息可以差分,则可以将一个询问拆成两个询问前缀相减直接扫描线解决。
例题
P3899 [湖南集训] 更为厉害
题目大意
给定一棵 \(n\)(\(n\le 3\times 10^5\))个节点的有根树,根节点为 \(1\) 节点。给定 \(q\)(\(q\le 3\times 10^5\))组询问,每组询问给定整数 \(p\) 和 \(k\),求满足以下条件的二元组 \((a,b)\) 的数量:
- 节点 \(a\) 和节点 \(p\) 的距离不超过 \(k\);
- 节点 \(b\) 同时是节点 \(a\) 和节点 \(p\) 的子节点;
- \(a\)、\(b\)、\(p\) 互不相同;
容易发现 \(a\) 和节点 \(p\) 总在一条返祖链上。先讨论 \(a\) 和 \(p\) 的祖孙关系:
- 若 \(a\) 是 \(p\) 的(真)祖先,则贡献为 \((sz[p]-1)\times \min(dep[p]-1,k)\);
- 若 \(a\) 是 \(p\) 的(真)子节点,则贡献为 \(\sum\limits_{dis(a,p)\le k,\ a\in subtree(p)}sz[a]-1\);
第一种情况可以直接计算,这里着重考虑第二种情况。我们发现,\(dis(a,p)\le k\) 且 \(a\in subtree(p)\) 可以抽象为 dfs
序的限制和深度的限制:
这正是一种二维数点问题。
- 将每个节点视为一个“修改”操作, \(dep[u]\) 作为扫描线的排序关键字,在数据结构的 \(dfn[u]\) 位置单点修改,权值为 \(sz[u]-1\);
- 将每个询问的 \(dep[p]+k\) 作为排序关键字,在数据结构上查询 \(\big[dfn[p]+1,dfn[p]+sz[p]-1\big]\) 的区间和,即为询问的答案;
我们将所有操作混合并按 key
排序,依次处理每个操作,这样就能保证满足查询操作的 \(dep\) 限制。将每个查询的结果贡献到对应的 ans
数组上,就能得到最后的答案。
关于线段树合并
本题也是线段树合并的模板题。线段树合并可以理解为:对 dfn
序扫描线,并且不要求信息对于 dfn
序可差分,同时使用线段树维护另一维度(也不要求可差分)。
如果一个树上问题包含二维约束,且信息不可差分,而其中一个维度是子树的限制,则无法使用扫描线+二维数点解决,但线段树合并可以实现。详见线段树合并。
代码
二维数颜色
题目大意
给定 \(n\) 个平面上的点,第 \(i\) 个点位于 \((x_i,y_i)\),颜色为 \(c_i\)。有 \(q\) 次询问,每次询问给出 \((a,b)\),询问 \(x\le a,y\le b\) 的点一共包含多少种颜色。
我们可以对 \(x\) 扫描线,用树状数组维护 \(y\)。那如何维护区间颜色数量呢?注意到询问都是 2-side
矩形,因此同种颜色的所有点中只需要考虑 \(y\) 最小的产生的贡献。这样,我们用一个桶数组记录每一种颜色的 \(y\) 坐标的历史最小值,也就是当前待在树状数组里的那一个。如果发现当前点的 \(y\) 更小,则删除原来的节点,加入当前节点。查询时直接查询前缀和即可。
P11364 [NOIP2024] 树上查询
题目大意
给定一棵有根树,根结点编号为 \(1\),每个结点 \(u\) 的深度 \(\text{dep}_ u\) 定义为 \(u\) 到 \(1\) 的简单路径上的结点数量。
除此之外,再定义 \(\text{LCA*}(l, r)\) 为编号在 \([l, r]\) 中所有结点的最近公共祖先,即 \(l, l + 1, \dots , r\) 的公共祖先结点中深度最大的结点。
你需要回答 \(q\) 个询问。每个询问给定三个参数 \(l, r, k\),需要求出
数据量级 \(5\times 10^5\)
重要结论:编号在 \([l_1,r_1]\) 区间内的所有结点的 lca
一定是 lca(i,i+1)
(\(i\in[l_1,r_1)\))中深度最小的一个。
记 \(b_i=dep[lca(i,i+1)]\),问题就转化为查找区间 \([l,r)\) 中[长度为 \(k-1\) 的连续子段的最小值]的最大值。而 \(k=1\) 的情况,使用 st
表预处理即可。
统计最小值的最大值,考虑每个数字作为最小值需要满足什么条件,再看当前询问是否满足这些条件,选取所有满足的数字中最大的一个。
具体的,我们先求出每个位置的 \(b_i\) 作为最小值的极大区间 \([x_i,y_i]\)(单调栈维护),分讨 \([x_i,y_i]\) 和询问 \((l,r,k)\) 的关系,有两种情况:
我们发现 \([x_i,y_i]\) 区间一共有 \(n-1\) 个,时间足够处理所有区间;同时这种约束是二维的,考虑使用二维数点解决。我们将两种情况分别统计,第一种情况按 \(y_i\) 和 \(r\) 扫描线;第二种情况按 \(k\) 和 \(y_i-x_i+1\) 扫描线。因为题目是询问满足条件的最大值,区间最大值需要使用线段树维护。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
|
P7302 [NOI1998] 免费的馅饼
题目大意
在平面的第一象限内有 \(n\) 个点 \((p_i,h_i)\),初始时你位于 \(x\) 轴正半轴的任意整数位置。设当前位于点 \((x,y)\),则下一秒你可以前往 \((x+a,y+1)\),\(a\in\{-2,-1,0,1,2\}\)。当你碰到第 \(i\) 个点可以获得 \(v_i\) 的分数。问最多获得多少分数。
值域 \(10^8\),点数 \(10^5\)。
考虑朴素 DP。设 \(f_i\) 表示走到第 \(i\) 个点处最多可以获得多少分数。写出状态转移方程:
时间复杂度 \(O(n^2)\),需要优化。
我们拆开绝对值
移项:
这正是一种二维数点问题。我们按 \(p_i+2t_i\) 为第一关键字降序排序,\(p_i-2t_i\) 为第二关键字升序排序。依次处理所有点,用树状数组以 \(p_i-2t_i\) 为下标,维护 \(f_i\) 的前缀最大值即可。
技巧
- 带有一个绝对值的式子可以拆成两个不带绝对值的式子;