树链剖分
Summary
树链剖分(Heavy-Light Decomposition,HLD)把每个非叶节点子树最大的儿子定为重儿子,连向重儿子的边称重边,首尾相接的重边构成一条重链。经过两遍 DFS 处理后,同一条重链上的节点 DFS 序连续,且树上任意一条路径至多经过 O(logn) 条重链。于是对路径与子树的修改、查询都能拆成 O(logn) 段连续区间,交给线段树等区间结构处理。文中给出两遍 DFS 的实现:第一遍求出每个点的父亲、深度、子树大小并选出重儿子,第二遍按重链优先的顺序分配 DFS 序,得到的剖分还能直接用于快速求 LCA。
参考资料
简介
树链剖分(Heavy-Light Decomposition,HLD)把每个非叶节点子树最大的儿子定为 重儿子,连向重儿子的边为重边,重边相连成 重链。两遍 DFS 后,每条重链的 DFS 序连续,且任意树上路径至多经过 条重链。于是树上路径与子树的修改、查询都能转化为 段连续区间,配合线段树解决。
实现
vector<int> G[N];
int fa[N],son[N],siz[N],dep[N];
int top[N],dfn[N],rnk[N],out[N];
int cnt=0;
void dfs1(int u)
{
siz[u]=1;
dep[u]=dep[fa[u]]+1;
for(auto v:G[u])
{
if(v==fa[u])continue;
fa[v]=u;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int t)
{
top[u]=t;
dfn[u]=++cnt;
rnk[cnt]=u;
if(son[u])dfs2(son[u],t);
for(auto v:G[u])
{
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
out[u]=cnt;
}
应用
详见 最近公共祖先。