Luogu P3379 最近公祖先(树链剖分)

最近公祖先(树链剖分)

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(ichildfap) ,那么 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(ichildp)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]]);	//把较深的迁移	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值