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 -