树的重链剖分
我们可以把一个树剖分为若干条链,按照如下规则剖分可以得到的结论是:树上任意两点间的的链数一定不超过 l o g n log_n logn条。证明略。
- 将每一个节点的重儿子和自己为一个链内。一个节点的轻儿子单独成为链的开端。
- 其中重儿子为 s i z e size size最大的节点,轻儿子为不是重儿子的儿子。
具体的代码可以这样实现:
void dfs1(int x,int fa)
{
size[x] = 1, dep[x] = dep[fa]+1;
for (int i=0,y;i<G[x].size();++i)
{
if ((y = G[x][i]) == fa) continue;
f[y][0] = x, dfs1(y,x), size[x] += size[y];
if (size[y] > size[son[x]]) son[x] = y;
}
return;
}
由于树链剖分的作用是处理树上的路径问题,因此我们有必要通过树链剖分将树转化为链。
如果我们进行深度优先遍历,且优先遍历重儿子,那么一条路径一定可以在树的DFS序中分成连续的若干段。我们也可以借此记录每一段----的段头。
就像这样:
void dfs2(int x,int cur)
{
dfn[L[x] = ++tot] = x, top[x] = cur;
if (son[x] > 0) dfs2(son[x],cur);
for (int i=0,y;i<G[x].size();++i)
{
y = G[x][i];
if (y != f[x][0] && y != son[x]) dfs2(y,y);
}
R[x] = tot;
return;
}
如果我们需要处理想点间的路径操作,我们可以这样实现:
void query(int x,int y)
{
while (top[x] ^ top[y])
{
if (deep[top[x]] < deep[top[y]])
x ^