Description
维护一棵树,每次需要执行以下两个操作:
- 新增一个叶子节点
- 查询新增的叶子节点与原树上的多少个点满足两点的距离小于等于两点点权相加之和。
强制在线。
Solution
前置芝士
- 高速平衡树(指除了\(Fhq-Treap\)和\(Splay\)以外的平衡树,或者你有高超的卡常技能也行)
- 动态点分治(替罪羊树式点分树)
前言
由于这道题涉及到的东西比较繁琐,所以接下来将分块讲解。
动态点分治
首先,如果这道题不强制在线的话要怎么做,我们观察一下题目给出的关于条件的式子:
\[ dis_{i,j}\le r_i+r_j \]
考虑重心\(u\),那么式子就变成了
\[ dis_{i,u}+dis_{j,u}\le r_i + r_j \]
移项
\[ dis_{i,u}-r_i\le r_j - dis_{j,u} \]
所以如果不强制在线,应该就得到了一个比较显然的做法:建立点分树,对于每个点维护一棵平衡树,存储\(r_i-dis_{i,u}\),对于一个新加入的点\(x\),就在点分树上暴力跳祖先,并进行查询和更新。
然而,本题要求强制在线(说的就是你,毒瘤的强制在线!)。
平衡树
因为本题中所用到的平衡树功能比较单一,所以可以写高速平衡树。
博主查阅了部分博文,发现主要有以下几种平衡树:
- 替罪羊树,重构因子为\(0.86\)时的时间复杂度比较优秀
\(Splay\)或\(Fhq-Treap\),高超的卡常技巧
- \(SBT\)
\(Treap\)
PS:当然,以上几种平衡树我并没有一一试验
替罪羊式点分树
前面说到离线的做法,既然强制在线,那我们就不能把点分树一次性全部建出来了。
那怎么办呢,每次加一个点就重构一次点分树不就行了。
考虑点分治时的分治中心,之所以要选在重心,是因为选在重心时效率最高,那么分治中心如果不在重心,显然也是可以的,所以每次新加入一个节点时,可以就将它加入到其在原树的父亲节点下面。
然后,为了保证复杂度,我们可以仿照替罪羊树的思想,设定一个重构因子\(\alpha\),当某个节点\(x\)的子树大小满足\(size_x>\alpha \cdot size_{fa_x}\)时,就暴力重构\(fa_x\)在点分树上的子树。
LCA
由于是动态加点,无法使用\(RMQ\)或树链剖分,只能使用倍增(倍增的胜利)。
时间复杂度及一些细节
本题的时间复杂度主要在外层点分树的重构,以及由于重构外层点分树内层平衡树的信息都必须重建上。
对于外层点分树,重构的时间复杂度每次均摊\(O(\log n)\)。
对于内层平衡树的重建,因为每个平衡树平均\(\log n\)个节点,每次插入时间复杂度\(O(\log n)\),所以重建一棵平衡树时间复杂度\(O(\log^2 n)\)。一次内层重建\(\log n\)棵平衡树时间复杂度是\(O(\log n^3)\)。
所以必须要使用高速平衡树,另外,由于一次重建的复杂度较高,所以点分树重建的因子建议设成\(0.9\)。
Code
咕咕咕