附上自学博客链接
https://www.cnblogs.com/Khada-Jhin/p/9576403.html
概念:
长链剖分类似于重链剖分,只是选择链的标准:
不是 子树节点最多的儿子,而是 到叶节点的路径最大的子节点
(所以说是长链)
顶点:一条长链上深度最小的点
性质:
对于一个有n个节点的树,
-
每一个子节点的最长链长度不长于他的祖先节点 (显然。。)
-
所有长链长度之和等于 n(总结点数)
证明:
一个点也算一条长链,每一个点在且只在一条长链上 -
从根节点走到任何一个节点,最多 n \sqrt{n} n 条长链(这是个大约数)
证明:
把一条长链上的点抽象成一个点,(总贡献为1)
所以,经过的每一个点都不在同一条长链上 ,那么走出的这条链长度为 k ,
有:
根节点一定有一个子节点,链长 ≥ k \ge k ≥k
根节点下一个点一定有一条子节点,链长 ≥ k − 1 \ge k-1 ≥k−1
…以此类推
所以走下去后,没有走过的点为 ∑ 1 k \sum_1^k ∑1k 约为 k 2 / 2 k^2/2 k2/2
所以k最大大约不过 n \sqrt {n} n
应用:
询问一棵树上任意一个点第k个祖先
O(n*logn)预处理+ O(1)在线回答
两次dfs,两次都类似重链剖分的dfs,只是转换条件换成了链长
void dfs1(int u){
len[u]=1;int v;
for(int i=1;(1<<i)<=dep[u];++i){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=head[u];i;i=e[i].nxt){
v=e[i].v;if(v==fa[u][0])continue;
fa[v][0]=u;dep[v]=dep[u]+1;
dfs1(v);
if(len[v]>mx[u])mx[u]=len[v],son[u]=v;
}
len[u]+=mx[u];
}
void dfs2(int u){
int v;
aft[top[u]].push_back(u);
for(int i=head[u];i;i=e[i].nxt){
v=e[i].v;if(v==fa[u][0])continue;
top[v]=son[u]==v ? top[u] : v;dfs2(v);
}
}
重点是询问环节,
维护:
我们对于每一条长链的顶端,维护两个 vector 数组
p
r
e
[
i
]
[
j
]
:
pre[i][j]:
pre[i][j]: 从i开始向上第j个祖先(没错,暴力维护。。)
a
f
t
[
i
]
[
j
]
:
aft[i][j]:
aft[i][j]: 从i开始向下长链上第j个后代
注意:
j
≥
1
j \ge1
j≥1 &&
j
≤
l
e
n
[
i
]
j\le{len[i]}
j≤len[i]
每一个顶点只维护所在长链长度个
由于长链和等于 n(第二条性质) 所以两个维护复杂度都是 O(n)
再对 1-n(数字)维护一个 highbit(即二进制位上最靠左的1的位置,可以和lowbit对比理解)
(这个维护不会超过 O(n*log你) )
操作:
对于 询问 第 x 号节点 的 第 k 个祖先 ,我们把目标祖先设为fa,
分类讨论:
1.fa和x在一条长链上,即
d
e
p
[
x
]
−
d
e
p
[
t
o
p
[
x
]
]
≥
k
{dep[x]-dep[top[x]] } \ge k
dep[x]−dep[top[x]]≥k
直接用
t
o
p
[
x
]
top[x]
top[x]的 aft 数组即可
2.fa 和 x不在一条长链上,即
d
e
p
[
x
]
−
d
e
p
[
t
o
p
[
x
]
]
<
k
{dep[x]-dep[top[x]] } \lt k
dep[x]−dep[top[x]]<k
这个时候我们跳到x的 向上
2
h
i
g
h
b
i
t
(
k
)
2^{highbit(k)}
2highbit(k)个祖先 ,设为 y
注意,剩下的
k
−
2
h
i
g
h
b
i
t
(
k
)
<
2
h
i
g
h
b
i
t
(
k
)
k-2^{highbit(k)} \lt2^{highbit(k)}
k−2highbit(k)<2highbit(k)
对于 y ,y所在长链长度一定
≥
2
h
i
g
h
b
i
t
(
k
)
\ge2^{highbit(k)}
≥2highbit(k) (根据第一条性质)
所以pre和aft都可以覆盖
对于y所在长链的顶端,即top[y]
如果在 fa上方,就和第一种情况相同
如果在 fa下方, 就用 top[y] 的 pre 数组 O(1)求即可
int get(int u,int k){
if(k>=dep[u])return 0;
if(dep[top[u]]<=dep[u]-k)
return aft[top[u]][dep[u]-dep[top[u]]-k];
u=fa[u][Log[high[k]]];
k-=high[k];
if(k>=len[u])return 0;
return dep[u]-dep[top[u]]>=k ? aft[top[u]][dep[u]-dep[top[u]]-k] : pre[top[u]][k-(dep[u]-dep[top[u]])];
}
代码:
总的代码写的很丑。。这里就不给了,其他的维护自己yy一下都很简单。。