树有很多种剖分。
常见的是重链剖分。
然而还有针对树深度信息维护的长链剖分。
和重链剖分类似。
长链剖分维护长链,同时也有长儿子和短儿子。
所有长链长度之和为(n),这个显而易见。
那么我们在维护和长度相关的信息的时候。
一般情况下是将儿子的长度位移,这样的话可以直接用指针数组继承长儿子的信息。
同时暴力统计短儿子的信息。
这样每条长链最多被扫一次。
复杂度就是完美的(O(n))。
T1.(Hotel)加强版
我们设(f[x][i])为距离(x)的长度为(i)的点的个数,(g[x][i])为有两个点在(i)的子树中,且其到达他们的(LCA)的距离为(d),他们的(LCA)距离(x)的长度为(d-i)的点对个数。
那么有:
[ans+=g[x][0]+g[x][i]*f[t][i-1]+f[x][i]*g[t][i+1],f[x][i]+=f[t][i+1],g[x][i]+=g[t][i-1]
]
然后这个东西直接做复杂度是(O(n^2))的。
考虑用长链剖分优化。
我们发现后面的转移其实都是一个位移。
我们用指针实现对长儿子的(shift),然后短儿子就暴力扫短儿子内部的最大深度。
这样每个长链就被扫一次,复杂度就是(O(n))的。
T2.谈笑风生
我们发现其实只需要关注(a,b)的情况。
1.(a)在上(b)在下,这个时候统计距离(a)的长度小于(k)的(b)的(sz_b-1)的和就可以了。
2.(b)在上(a)在下,这个时候统计(a)的满足限制的祖先个数即可。
第二个很简单。
考虑第一个用长剖维护。
(dp[i][j])为距离点(i)的长度为(j)的(sz_b)和。
那么前缀和不好维护,维护一个后缀和就可以(O(n))的(dp)了。