P3920 [WC2014]紫荆花之恋

题目链接

耗时4.5h的大工程。

这题真的不难

树上路径问题首先想点分治。并且这道题需要支持动态加叶子。如果动态加叶子的话,可能会使得点分数不够“平衡”。我们可以模仿 K-D Tree 的方法(类似替罪羊树),如果某个叶子节点占其父亲的 (siz) 的比值超过了 (alpha),那么我们就将这个子树暴力拍扁,重新构建。由于一次暴力拍扁是 (O(nlog^2n)) 的,因此我们的 (alpha) 值需要设得稍微大一些(比如 (0.8))。

具体来说,这道题要对新加的点 (i),找到符合 (dis(i,j) <= r_i + r_j) 的所有 (j)。我们考虑在 (i,j) 路径经过的分治重心上计算贡献,则有:

[dis(i, rt) + dis(j, rt) <= r_i + r_j ]

[dis(j, rt) - r_j <= r_i - dis(i, rt) ]

按照惯例,我们设 (f1(i)) 数组表示分治区域内各个点到分治重心 (i) 的距离 - 点权 的权值数组;(f2(i)) 数组表示分治区域内各个点到分治重心的点分树上父节点 (fa(i)) 的距离 - 点权 的权值数组,每次每层容斥求解,然后将(i) 的信息插入到 (f1), (f2) 数组里。 (f1, f2) 数组用平衡树维护。这里我用的替罪羊树(好写且常数小)。

针对插入叶节点,我们就像上面一样一直跳点分树的 (fa),计算并更新平衡树。如果我们计算贡献的时候发现这个点在点分树上的子树不平衡,那么我们就将这个点暴力拍扁,即重新做一遍普通的点分治。

细节比较多,比如说,显然这题要维护点分树,在拍扁以后我们可能需要把点分树的子树暴力重构,把子树和原来的父节点之间的边删除换掉。这个换边操作可以直接用 (vector) 实现,找到与 (fa) 连接的子树上的节点,更改 (vector) 里面存的编号即可。

原文地址:https://www.cnblogs.com/JiaZP/p/13378450.html