树链剖分各数组的作用:
son[]:最重的儿子节点,即节点最多的那个
bulk[]:所有儿子节点的总和(包括儿子的儿子)
dep[]:该节点的深度
ft[]:该节点的父亲
top[]:链的最上方的节点
首先Dfs预处理出前面的四个数组。
然后第二个Dfs进行剖分,预处理出最后一个数组。
例子:
此时son[1]=3(因为bulk[3]=6>bulk[2]=5),son[3]=10,son[4]=5,以此类推。
然后剖分之后相当于变成了
此时top[10]=1,top[7]=7,top[8]=9,以此类推
接下来就是进行Lca,假设c=lca(a,b),首先,当a,b在同一条重链中时,c=min(dep[a],dep[b]),当a,b不在同一条重链中时,画图易知c一定是划分轻重链的的那个分支点,比如图中的1,3,4。假设我们现在要求lca(7,8),那么lca(7,8)一定是某条直到7,8在同一条重链时的分支点,所以不断地把7,8向上合并到重链中,直到他们在同一条重链中。
现在进行模拟,先看看代码中的Lca,方便理解。令x=7,y=8,因为dep[top[x]]>dep[top[y]](top[x]=7,top[y]=9),所以更新x=4,发现top[x](top[x]=2),top[y](top[y]=9)不在一条重链中,继续更新,此时dep[top[y]]大,更新y=3,因为dep[top[x]]大,更新x=1,发现他们已经在同一条重链中,那么深度小的即为lca(7,8)
附模板:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000;
struct Edge
{
int v, next;
Edge(int v=0,int next=0):v(v),next(next){}
}edge[MAXN];
int head[MAXN], edgenum;//邻接表
int son[MAXN], bulk[MAXN], dep[MAXN], ft[MAXN], top[MAXN];
void toInit()
{
memset(head, -1, sizeof(head));
edgenum = 0;
}
void toAdd(int u,int v)
{
edge[edgenum] = Edge(v, head[u]);
head[u] = edgenum++;
}
void toDfs1(int u,int f,int tier)
{
son[u] = 0;dep[u] = tier;ft[u] = f;bulk[u] = 1;
for (int i = head[u];i != -1;i = edge[i].next)
{
int v = edge[i].v;
if (v == f) continue;
toDfs1(v, u, tier + 1);
bulk[u] += bulk[v];
if (bulk[v] > bulk[son[u]])//son[]更新为重儿子
son[u] = v;
}
}
void toDfs2(int u,int f,int rt)
{
top[u] = rt;
for (int i = head[u];i != -1;i = edge[i].next)
{
int v = edge[i].v;
if (v == f) continue;
if (v == son[u])//链剖分
toDfs2(v, u, rt);//重链top继承父亲的top
else
toDfs2(v, u, v);//轻链top为自身
}
}
int toLca(int x,int y)
{
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
swap(x, y);//使深度大的始终为x
x = ft[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int main()
{
return 0;
}