经常口胡长链剖分 $O(n)$,现在算是见识了
# 题面
> Linked 洛谷 P5904
点击展开/折叠 题面
给出一棵有 $n$ ($1\le n\le10^5$)个点的树,求有多少组无序点对 $(i,j,k)$ 满足 $i,j,k$ 两两之间的距离都相等。
# 解析
三个点 $(x,y,z)$ 两两之间的距离一定可以表示成下面这种形式:
即下面这种情况:
然后因为两两距离相等,可以推得:
令 $\text{dist}(x,lca)=\text{dist}(y,lca)=i$ 则有
于是 $\text{dist}(lca,z)=i$。
所以我们可以DP维护两个值:
- $f_{u,i}$:$u$ 子树内与 $u$ 距离为 $i$ 的点数;
- $g_{u,i}$:$u$ 子树内的点对 $(v,w)$ 的数量,满足:
- $\text{dist}(lca,v)=\text{dist}(lca,w)$;
- $\text{dist}(lca,u)=\text{dist}(lca,v)-i$。(这样设计方便计算——可以直接把 $g_{u,i}$ 和 $f_{u,i}$ 配对得到答案)
于是树形DP过程中,可以在合并子树时计算答案。设 $u$ 要合并子树 $v$,则
接下来考虑优化,发现 $f,g$ 第二维的取值范围均是 $[0,dep_u)$(这里的深度定义是该子树最深的节点的相对深度),于是可以长链剖分优化。
根据长链剖分的原理,每个点恰好属于一条长链,且每条长链只会在链顶作为“轻儿子”暴力合并的时候会有链长的复杂度,链长和是 $n$,则复杂度 $O(n)$。
然后就是一些小技巧,比如分配内存池,用指针比较快乐,只是注意分配的 siz(设 $v$ 是 $u$ 的重儿子):
- 对于 $f_u$,$f_{u,i+1}:=f_{v,i},f_{u,0}=1$,则可以直接把 $f_v$ 的初始指针指向 $f_u+1$,一条长链只需要链长的空间;
- 对于 $g_u$,$g_{u,i}:=g_{v,i+1}$,因此需要把 $g_{v}$ 的初始指针指向 $g_{u}-1$,这样所需空间是链长两倍的。
# 源代码
1 | /*Lucky_Glass*/ |
THE END
Thanks for reading!
> Linked 零和Zero-Sum