点分树(动态点分治)
点分树是指从上一级分治中心指向下一级分治中心的(虚)边组成的树。
性质:对于任意两点 \(x,y\),它们在点分树上的 \(\operatorname{lca}\) 存在于它们在原树的最短路径上;
由于点分树也涵盖了所有节点,并且深度比较小(最多 \(\log n\) 层),因此我们可以枚举操作节点 \(x\) 在点分树上的祖先 \(t\),然后统计以 \(t\) 为点分树上 \(\operatorname{lca}\) 产生的贡献。
我们可以在分治中心处维护一个数据结构,维护对应连通块内的节点信息,来实现动态的修改查询。
P6329 【模板】点分树 | 震波
题意
给定一棵 \(n\) 个节点的树,点有点权 \(a_i\),你需要支持 \(q\) 次操作,每次操作是如下两种中的一种:
- 给定 \(x,d\),求 \(\sum_{dis(x,y)\le d}a_y\);
- 给定 \(x,y\),执行 \(a_x\gets y\);
强制在线,\(n,q\le 10^5,\ 0\le d\le n-1\)
考虑点分树,对每个分治中心,我们用数据结构维护连通块内部所有节点到它的距离信息。对于查询操作,假设查询 \((x,d)\),我们在点分树上从 \(x\) 开始往上跳,对于 \(x\) 在点分树上的祖先 \(t\),我们查询 \(t\) 对应的连通块内到 \(t\) 距离不超过 \(d-dis(x,t)\) 的点的点权和。
然而这样查询会有一个问题,在处理 \(t\) 连通块时,\(t\) 向 \(x\) 方向的(原树)子树内的节点的距离计算有误,因为它们与 \(x\) 的(点分树)\(\operatorname{lca}\) 不是 \(t\);并且它们的贡献已经在 \(t\) 的下一级位置统计过了;因此在 \(t\) 处需要去掉它们的贡献。
因此,我们再用另一个相同的数据结构维护出每个节点的连通块向它(点分树)父亲的贡献。不难发现,一个节点 \(u\) 对应的第一个数据结构(整个连通块的信息)等于它的所有(点分树)儿子的第二个数据结构信息的并,再加上 \(a[u]\)。
这样,每次在祖先 \(t\) 处查询时,我们再额外在第二个数据结构上查询 \(t\) 向 \(x\) 方向的(点分树)儿子对应的连通块中,有多少个到 \(t\) 距离不超过 \(d-dis(x,t)\) 的节点。
上面提到的信息使用树状数组维护即可,用 vector
动态开空间,空间复杂度 \(O(n\log n)\),时间复杂度 \(O(n\log^2 n)\)。
核心代码
完整代码
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 207 208 209 210 211 212 213 |
|
P2056 [ZJOI2007] 捉迷藏
题意
给定一棵 \(n\) 个节点的无根树,每个节点上都有一盏灯,灯只有点亮熄灭两种状态。初始时所有灯都是熄灭的。
有 \(q\) 次操作,每次操作是如下两种中的一种:
- 给定 \(x\),反转节点 \(x\) 处灯的状态;
- 查询最远的两个熄灯的节点之间的距离,若没有输出 \(-1\),只有一个输出 \(0\);
\(n\le 10^5,\ q\le 5\times 10^5\)
考虑点分树,如何统计贡献。对于一个节点 \(t\),我们枚举它的两个子树,并分别从两个子树内选出两个距离最远的节点。那么我们如何去除重复子树的贡献呢?注意到由于我们只需要最大值,因此无需把整个连通块内的所有节点都放到分治中心 \(t\) 的数据结构里。我们只需找到每一个子树连通块内到 \(t\) 最远的关灯节点,将它放到分治中心 \(t\) 的数据结构里。这样,每棵子树最多贡献一次。
我们可以使用堆来存储每棵子树中最远距离,查询时只需要取出堆中的最大值和次大值相加即可。
为了维护出每棵子树(连通块)内部到上一层分治中心 \(t\) 最远的熄灯节点,我们给每个节点再开一个堆,维护所有熄灯节点到 \(t\) 的距离;每棵子树都会把堆顶元素贡献给分治中心 \(t\) 的第一个堆。
为了维护答案,我们再在全局开一个堆,将每个节点作为 \(\operatorname{lca}\) 时的最优答案装进这个堆里,查询时取这个堆的堆顶即可。
因为带修,所以使用可删堆。
代码
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 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
|