最近公祖先(树链剖分)
What is 最近公祖先?
对于树上节点n,m,最近公祖先指的满足同为n,m祖先的最深节点。
最近共祖先的树链剖分解决
首先先介绍树链剖分:
树链剖分可以说是一种解题的思想或是策略,其实质是把一个二维的树拆成一个个单独的链,再利用其它数据类型维护,把一个复杂的树上任务分割成较为简单的链上子任务。(其实这道题连板子都不算,没有重编号)
先说说怎么拆分吧:
定义:
s
i
z
e
p
size_p
sizep指以
p
p
p为根的子树大小,
f
a
p
fa_p
fap指
p
p
p的父节点,
d
e
p
p
dep_p
depp指
p
p
p的深度,
t
o
p
p
top_p
topp指在
p
p
p,集合
c
h
i
l
d
p
child_p
childp,
s
o
n
p
son_p
sonp指
p
p
p的重儿子
如果 s i z e p > s i z e i ( i ∈ c h i l d f a p ) size_p>size_i(i \in child_{fa_p}) sizep>sizei(i∈childfap) ,那么 s o n f a p = p son_{fa_p}=p sonfap=p
性质1: t o p s o n p = t o p p top_{son_p}=top_p topsonp=topp
性质2: p ≠ s o n f a p , t o p p = p p\neq son_{fa_p},top_p=p p=sonfap,topp=p
性质3: d e p p = d e p f a p + 1 ( d e p r o o t = 1 ) dep_p=dep_{fa_p}+1(dep_{root}=1) depp=depfap+1(deproot=1)
性质4: s i z e p = ∑ i ( i ∈ c h i l d p ) s i z e i + 1 size_p=\sum_{i(i \in child_p)} size_i+1 sizep=∑i(i∈childp)sizei+1
则对于同一链上的两点 u , v ( d e p u < d e p v ) u,v(dep_u<dep_v) u,v(depu<depv) 则 则 则u 一 定 是 一定是 一定是v 的 祖 先 , 只 需 要 每 次 将 较 深 点 的祖先,只需要每次将较深点 的祖先,只需要每次将较深点p 跳 到 跳到 跳到fa_{top_p}$ ,当这两点处于同一链上时,较浅的点就是 u , v u,v u,v的最近公祖先
代码实现:
先链式前向星存进
void set1(int p,int f)
{
fa[p]=f;//找爸爸
dep[p]=dep[f]+1;//性质3
w[p]=1;//子树包含自己
for(int i=head[p],t;i;i=e[i].next)
if((t=e[i].to)!=f)//不是爸爸
{
set1(t,p);//自己是爸爸,路径终点就是儿子
w[p]+=w[t];//性质4
if(w[t]>dep[son[p]]||son[p]) son[p]=t; //重儿子
}
}
void set2(int p,int tp)
{
top[p]=tp;//找top
if(son[p]) set2(son[p],tp);//性质1
for(int i=head[p],t;i;i=e[i].next)
if((t=e[i].to)!=son[p]&&t!=fa[p])//不是爸爸不是重儿子
set2(t,t);//性质2
}
int solve(int a,int b)
{
if(top[a]==top[b]) return dep[a]<dep[b]? a:b;//在同一链上就是较浅节点
return dep[top[a]]>dep[top[b]]? solve(fa[top[a]],b):solve(a,fa[top[b]]); //把较深的迁移
}