举办乘凉州喵,举办乘凉州谢谢喵

· · 个人记录

f_{x,d} 表示 x 子树内x 距离 \le d 的点数,g_{x,d} 表示 x 的轻儿子的所有子树内与 x 距离 \le d 的点数。

不妨先来考虑 yx 祖先的情况,考虑如何算出此时 y 子树内有多少个点与这条链距离 \le d。发现这其实就是链上的点数,加上,链上每个点 u 的其他儿子的子树内,与 u 距离 \le d 的点数,之和。

考虑这条链 y\to x,发现链上的大部分边都是重边,只有 O(\log n) 条轻边。这也就意味着,如果我们要计算与这条链距离 \le d 的点数,对于大部分点,都只需要计算他们轻儿子内的信息;只有 O(\log n) 个点需要特殊处理重儿子的信息。具体来说,设这些轻边分别是 (u_1,v_1),\cdots,(u_k,v_k),其中 v_iu_i 的父亲,那么答案就应该是链上所有点的 g_{\cdot,d} 之和,减去 \sum f_{u_i,d},再加上 \sum f_{\text{hson}(v_i),d}

考虑到 f 那部分一共只有 O(\log n) 项,而再 O(\log n) 时间内计算单点的 f 自然是简单的,于是这部分容易做到 O(n\log^2n)。现在考虑如何计算一条链上所有点的 g_{\cdot,d} 之和。

考虑差分成 1\to x 链上所有点的 g_{\cdot,d} 之和,我们注意到每个点的所有轻儿子的子树大小之和为 O(n\log n),这允许我们从根开始往下 DFS,每次遇到一个点,就遍历他的所有轻儿子的子树内的所有点,并计算他对 g 的贡献。具体来说,我们动态地维护序列 G_i=\sum_{u\in\text{path}(1,x)}g_{u,i},每次新进入一个点 x 时,暴力扫他的轻儿子的子树内的每个点 v,并令 G_{\text{dis}(v,x)}\leftarrow G_{\text{dis}(v,x)}+1。则查询只需查询前缀和,使用树状数组维护即可,时间复杂度 O(n\log^2n)

最后,由于实际距离 \le d 的点可能在子树外,我们还需要使用某些树分治算法进行一遍树上邻域数点。这部分也容易做到 O(n\log^2n) 以内。

最后的最后,我们实际上询问的并非祖先-后代链;设实际询问的是 x\to y 这条链,z=\text{LCA}(x,y),我们分别对 z\to x,z\to y 算出 z 子树内与这条链距离 \le d 的点数,那么多算的部分恰好就是 z 子树内与 z 距离 \le d 的点数,即 f_{z,d}。最后加上 z 子树外与 z 距离 \le d 的点数即可。

https://qoj.ac/submission/108377