点分治杂题

点分治杂题

P10809

其核心是三元环。

一个观察是,每个三元环组成一个极大点双连通分量,这启发我们建立圆方树。

很自然地,圆方树的重心应当是最后连接的三元环所代表的点双连通分量。

那么检验就很明确了:建立圆方树,找出重心,检查边数,点数,以及这个重心是否仅有三个等大小的子树。

这样就能够做到将三个子图划分出来,递归处理即可。

P6976

这种在特殊图上询问多个点对的最短路问题,往往通过分治,将原点集划分为两个,并找到两个点集之间通行的必经之路,以此分治处理。

三角剖分的凸多边形,可以以一条剖分边及其端点将原凸多边形划分为两部分,网格图也是这样,通过一行一列进行划分。

那么,主要到三角剖分转对偶图之后是一棵树(删掉外平面与内平面的边),且度数不超过3,那么可以随机取一条划分边进行分治,从端点跑bfs再对跨越的询问处理答案即可。

P6326

本质是要求找到一个子连通图并在其中每个店都买东西。

事实上是特殊的树上依赖背包,考虑在钦定一个点在连通块中,然后做树上依赖背包(本质是,将父亲的dp数组作为儿子dp的初始数组,然后加入取儿子的方案,回溯时对应位置取 \(\max\) 即可),利用单调队列优化多重背包。

利用点分治优化枚举过程即可。

\(O(nm\log n)\)

P10805

复杂树上路径问题,考虑点分治。

考虑常见的正反扫描技巧处理。难点在于如何处理每次加油。

事实上,一个汽车某个点加油之后,就与从这个点出发的汽车后续情况相同了。

\(g_{u},h_u\) 分别表示子树内往上走从多少个点出发需要在 \(u\) 处加油,从子树外往 \(u\) 走需要多少个点在 \(fa_u\) 加油。

使用栈上二分和线段树分别维护 \(g,h\) 即可。

有了 \(g,h\),答案计算自不必多说。

P7215

与上上题一致,若钦定某个点在连通块内,如何找到覆盖它的最小连通子图?

以这个点为根,维护一个队列,每次加入一种颜色,取出队头,将这种颜色的点到这个点的路径上所有点标记,并将新颜色加入队列。实现上只需要利用并查集维护往上跳的第一个未标记点即可。

利用点分治优化处理即可。

注意实现,不要让复杂度假掉。

P3241

明显应该使用点分树。

对每个分治中心开线段树,用线段树解决 \([L,R]\) 这个偏序问题,然后求距离和,利用自己与父亲的距离进行容斥即可。

P4775

如何处理路径有边相交是重点。

注意到 lca 不同时,相交路径是一条直链,这告诉我们可以直接拆掉一条路径为两条链 \((lca,v,w),(lca,u,w)\)

这个路径的长度直接算在 \(w\) 里,我们只需要处理相交部分的贡献,也就是 \(d_{lca(x_1,x_2)}-\max(d_{l_1},d_{l_2})\),其中 \(l\) 是链顶,\(x\) 是链底。

考虑在树的 \(x\) 点处加入二元组 \((l,w)\),则\(lca\) 可以通过不同子树以及自己这个点的二元组固定为自己。以 \(d\) 为键值加入线段树,对线段树进行合并,每次合并时用右侧的 \(w+d\) 与左侧的 \(w\) 来计算答案。单点插入是一样的。注意到链必须相交,那么我们考虑计算完之后,将 \(l\) 为当前点父亲的点全部删掉。这一步可以通过对 \(d\) 做双关键字排序得到每个 \(l\) 在线段树上独享一个位置,直接删掉线段树这个叶子即可。

\(lca\) 相同时,两条相交路径至少各有一个端点位于 lca 的同一子树内,考虑建立虚树进行处理。

设两条路径 \((x_1,y_1),(x_2,y_2)\) 的 lca 相同,且 \((x_1,x_2)\) 位于 lca 的同一子树内,那么这个配对方案的贡献也可以写为 \(\frac{1}{2}(dis(x_1,y_1)+dis(x_2,y_2)+dis(y_1,y_2)+dis(x_1,x_2))-w_1-w_2\)

\(dis(x_1,x_2)\) 拆分为 \(d_{x_1}+d_{x_2}-d_{lca(x_1,x_2)}\)

除开 \(dis(y_1,y_2),d_{lca(x_1,x_2)}\),两条路径的附加代价是可以拆开的,那么 \(dis(y_1,y_2)\) 带上附加条件,其实就是一个树的带权直径问题,根据带权直径的结论,对于每一颗子树,维护其子树内 \(x\) 点对应的 \(y\) 点的带权直径,\(lca\) 就可以在合并各子树带权直径的过程中固定了,进而就可以计算答案了。

code

P9678

很经典的支配点对应用。

显然,若对于 \(l_1\le l_2,r_2\le r_1\),若 \(dis(l_2,r_2)\le dis(l_1,r_1)\)\((l_1,r_1)\) 无用。

我们想要以此让可能成为最优决策的决策尽量少一点。

但是 \(dis\) 不好拆,因为存在 \(lca\),为了拆掉它,我们采取点分治,将其变成 \(d_{x}+d_{y}\)

点分治,设 \(d\) 为点到分治中心的距离,由于 \(dis(x,y)\le d_x+d_y\),故一个可能成为最优决策的决策,即使在本层没有选出,在下层同样可以被选出。

那么我们想要的可能成为的最优决策的决策 \((a,b)\) 就转化为了不存在 \((x,y)\) 满足 \([x,y]\subset [a,b]\)\(d_x+d_y\le d_a+d_b\)

这里由于可以取等可以推出 \(d_x\le d_a,d_y\le d_b\) 都是不可行的。

这说明了 \([a,b]\) 的严格最小值与不严格次小值都在端点上。

如何处理呢?可以使用单调栈,在任意时刻,单调栈的相邻两个元素组成的区间是好的。那么我们只需要在单调栈加入元素时,将弹出的与这个新加入的计入,然后弹出完成之后将新加入的这个元素及其栈内的前一个元素计入即可

最后可以处理出 \(O(n\log n)\) 个点对,再利用扫描线就可以做到 \(O(n\log^2 n)\) 了。

P9544

根据 合金 的结论:

给出 \(a_{1\sim n},b_{1\sim n}\) 以及 \(A,B\),则:

\[\begin{cases} \sum a_ir_i=A\\ \sum b_ir_i=B\\ \sum r_i=1\\ \end{cases} \]

存在非负数解组 \((r_1,r_2\dots r_n)\) 的充要条件是点 \((A,B)\) 位于 \((a_i,b_i)\) 组成的凸包内。

在本题中,由于 \(x_i+y_i+z_i\) 相同且等于 \(A+B+C\),所以可以化为上述方程形式。

在这个基础上来研究这个问题:

存在最优方案使得最多有三个 \(r \neq 0\)

考虑任取凸包或者凸包的一点,连接 \((A,B)\),则必然这条直线会将凸包分为两半,取两侧分别与这条直线夹角最大的点,即可构造出一个三角形包含 \((A,B)\),假设不包含,由于这选出的两个点必然在凸包上相邻(其实就是这条直线与凸包相交的边与选出的点更远的那一个,若与凸包上的点直接相交,则只需要两个点),若不包含则必然在凸包外。

证毕。

最优方案在树上一定是一条链

由上一个命题,我们知道最多有三个 \(r\neq 0\),则若存在一个凸包,所用到的点在树上不是一条链,则能够找到一个分叉点(即在选出的图里度数>2),将这个作为上个命题证明中选出的那个点,我们一定能够在图中仅再选出两个点使得满足要求。

选出三个点及其连通它们的最小连通块后,假设它仍然不是一条链,则必然存在一个中心度数为三,以它为上述命题中选出的那个点,即可化作一条链。

在最优方案的链上,第三点任意选除了端点外的所有点都合法

由第一个命题,凸包内或凸包上任选一个点都可以找到一个三角形包含 \((A,B)\),则设一个最优的方案,我必然能够在链上除端点任选一个,且选出的另外两个点一定是这条链的端点。如果不是,则可以缩短这条链。

到了这一步,已经可以做了。

考虑点分治,由第三个命题,我们可以固定选分治中心 \(rt\)

由第二个命题,我们无需担心选出的另外两个点在 \(rt\) 的同一个子树中,如果是,那么继续分治可以找到更优的解,且此刻 \(d\) 的和大于选出后的连通块大小,也就是我们可以直接使用 \(d_x+d_y\) 计算另外选点 \(x,y\) 的代价。

那么解决方案就很明确了。

首先做一次 dfs,判掉只需要另外选一个点的情况。

然后将所有点分为在 \(rt-(A,B)\) 这条直线的两侧。选同侧点必然是无法包含 \((A,B)\) 的,选两个异侧点 \(x,y\),就需要满足角 \((rt,(A,B),x)+(rt,(A,B),y\ge \pi\)

为了方便实现,且注意到我们只关心角度,则可以将 \((A,B)\) 设为原点,然后将所有点都变成原点到它的射线与单位圆的交点,这不影响答案

然后问题就变为分别算出 \(b_i=(rt,(A,B),x)\) ,然后将点分到两侧,求最优的 \(x,y\) 在异侧,满足 \(b_x+b_y\ge \pi\) 的情况下 \(d_x+d_y\) 最小。

排序后双指针即可。桶排 \(d\) 可以做到 \(O(n\log n)\)

P11343

这个应该算技巧型问题,它给出了处理树上复杂路径类型的 dp 的一个通法:利用点分树寻找最优决策。

首先我们很容易得到 \(dp\) 式:设 \(f_i\) 为从根节点出发最后坐车到 \(i\) 的最小费用。

\[f_x=\min(f_y+A_y+B_ydis(x,y)) \]

显然,我们只能考虑优化这个没有顺序的转移,首先就需要研究转移顺序。

虽然处理的一般手段是每次取 dp 值最小的来转移,但这张图达到了 \(O(n^2)\) 级别且具体顺序模糊太复杂。

一个明显的性质是,最优解里换乘的车,其 \(B\) 必然是严格递减的。

因此可以按 \(B\) 排序,从大到小转移。但注意到最后一次换乘并不要求终点的 \(B\) 更小。

这也只限于最后一次,因此在完成转移后,单独转移一次即可,这时候转移的顺序已经不重要了(最多只会换一次车)。

顺序有了,如何快速转移?

考虑建立点分树来查找每次转移的最优决策,将 \(dis(x,y)\) 拆解为 \(d_x+d_y\)

则点分树只需要执行若干操作:

  1. 加入决策 \((f_x+A_x+B_xd_x,B_x)\)
  2. 查询对于 \(d_x\),在所有决策 \((b,k)\) 中找到 \(kd_x+b\) 最小的那一个。

在点分树每个节点维护一个李超树即可做到。

往往这样做空间复杂度较高,需要两个 \(\log n\),即使不满。

那么我们时空双 $\log $ 解决了这个问题。

最后就是对所有点再查一次最优决策即可。

P5984

虽然写着比较难受,但想法比较有意思。

这类高进制数的比较,基本只剩下主席树维护哈希一条路。注意到不会存在进位,所以可以做。

维护出每个 \(d\) 直接求第 \(k\) 小肯定不可能,就像树上路径第 \(k\) 小,也是通过点分治求排名+二分实现的。

那么可以套用这个模式,二分+点分治。

点分治

如何做到最好?

照搬路径统计的话,就是每层给 \(d\) 排序,然后双指针统计,然后容斥一下。可以做到 \(O(n\log^2 n\log V)\)

注意到没必要每次都去重新排序,可以提前拿出,则预处理 \(O(n\log^2 n\log V)\),单次询问 \(O(n\log n\log V)\)

二分

朴素二分是 \(\log n^n=n\log n\) 的,必然爆炸。

不过这种方法的实现可以参考对于 \(n\) 进制数从高位到低位每一位单独二分。

如何优化? 值域范围大得离谱,但点数其实并不多时,可以考虑随机二分

也就是当前 \([L,mid,R]\),二分后在下一个二分区间随机选一个 \(mid\) 作为中心点二分。

这样我们只需要支持查找在这个区间内的某个点即可,注意保证随机找。

这其实很简单,在点分治双指针部分可以顺带维护。注意随机性。由于点太多,可以在每一层的每个点作为端点时,在合法范围随机选另一个,最后在 \(O(n\log n)\) 个点中随机选条路径即可。

因此二分期望复杂度 \(O(\log n)\)

实现上,由于有值相同的点存在,不好判断,可以采用固定二分次数。

实现坑点:注意主席树比较时可能需要传多个点进去,这时候不要使用 vector,array,常数大得出奇。

P5311

在思考时我陷入了一个思维陷阱:总是想点分树,往上跳,每一层做合并啊,容斥啊之类的。

但其实注意到,在本题中,将 \(x\) 改为其所在连通块任意一个点都是等价的。

那么我直接在点分树上找到最浅点(注意并不是往上跳后一定合法),将 \(x\) 改为它,则就去掉了当前连通块可能无法包含答案连通块的问题,现在每个分治中心的问题独立了,且变成了序列问题。

给出 \(n\) 个点 \((l_i,r_i,c_i)\),询问给出 \([L_i,R_i]\),求集合 \(\lbrace c_i|[l_i,r_i]\subseteq [L,R]\rbrace\) 的大小。

解决这个问题是经典的。

离线,将所有都按 \(l\) 从大到小排序。

依次扫描,

  • 新加入的点 \((l_i,r_i,c_i)\)\(c\) 还未出现过,此刻直接在 \(r\) 处打上 \(+1\) 的标记
  • 新加入的点 \(c_i\) 出现过,则若新的 \(r\) 更小,撤掉以前的标记打上这个。
  • 查询就查 \([1,R_i]\) 的标记个数

树状数组就够了。

P9260

纯 shift 题。但在去年省选前做到了这个就好了。

首先显然需要点分治内部前缀优化建图,接着是缩点,最后利用 bitset 跑可达性统计。

\(O(\frac{n^2\log n}{w}+n\log n)\)

空间爆炸,将数字按 512 分组跑即可(联合省选2025的泪啊,只差这个)。

常数优化上,注意前缀优化建图时只建有用的点,能快四五倍。

posted @ 2025-07-21 20:31  spdarkle  阅读(20)  评论(0)    收藏  举报