LCA倍增法 求树的最近公共祖先(模板)

LCA倍增法

求树的最近公共祖先

查询树上任意两个节点之间的路径信息,实际上就是找这两个节点的LCA,因为唯一路径就是这两个节点分别到LCA会合,所以关键问题就是求LCA,需要的路径信息可在找LCA过程中维护。找LCA的思想类似于RMQ,定义fa[u][i]为节点u的第2^i个父节点,fa[u][0]就是u的父节点,fa[u][1]就为u的爷爷节点,那么就有如下递推式:

fa[tmp][i] = fa[fa[tmp][i - 1]][i - 1];,可以理解为tmp到2^i个父亲可以拆成先到2^(i-1)父亲处,再从该父亲处到相应的2^(i-1)父亲处。

路径上的权值总和也可以这样得出:

gw[tmp][i] = gw[tmp][i - 1] + gw[fa[tmp][i - 1]][i - 1],可以理解为u到2^i个父亲的路劲和可以拆成先到2^(i-1)父亲处的路径和,再从该父亲处到相应的2^(i-1)父亲处的路径和。

先通过bfs预处理出每个节点u的fa[u][i],gw[u][i]和深度并记录,通过不断上升节点来找到LCA和路径权值总和。

hihoCoder 

#1062 : 最近公共祖先·一

题目链接

题意:给你n对父子关系,然后有m组查询,每一组查询包括两个人名,输出它们的最近公共祖先,没有输出-1.

ps:如果有一个人都没出现过或者两个人都没出现过并且不是同一个人,则不知道它们的祖先关系,如果两个人没出现过但它们是同一个人,则要输出他自己。

思路:通过并查集将每一个人的fa[]映射到自己的最终祖先上,建立一个树的根结点,将所有的目前的子树的根结点(每个族谱的最终祖先)连接起来,然后,就是lca求每一个结点的最近公共祖先了。如果是树的根结点的话,意味着它们没有最近公共祖先。

#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<map>
using namespace std;
const int maxn = 100010;     
const int DEG = 20;       //树的最多层数
struct Edge {             //边
	int to, next, w;
} edge[maxn * 2];
int head[maxn], tot;      //前向星链表,head[]为链头,tot为边的总数
int gw[maxn][DEG];        //gw[u][i]表示结点u到2^i个父亲结点的路径总权值
int sum_path;             //sum_path为全局变量
void addedge(int u, int v, int w) {  //前向星链表添加边
	edge[tot].to = v;
	edge[tot].next = head[u];
	edge[tot].w = w;
	head[u] = tot++;
}
void init() {       //初始化
	tot = 0;        
	memset(head, -1, sizeof(head));
}
int fa[maxn][DEG];//fa[i][j]表示结点i的第2^j个祖先
int deg[maxn];//深度数组
void BFS(int root) {     //bds预处理出每一个结点u的fa[u][i]和gw[u][i]以及深度
	queue<int>que;
	deg[root] = 0;
	fa[root][0] = root;
	que.push(root);
	while (!que.empty()) {
		int tmp = que.front();
		que.pop();
		for (int i = 1; i < DEG; i++){       //u到2^i个父亲可以拆成先到2^(i-1)父亲处,再从该处到相应的2^(i-1)父亲处
			fa[tmp][i] = fa[fa[tmp][i -
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值