题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

整体步骤:1.使两个点深度相同;2.使两个点相同。

这两个步骤都可用倍增法进行优化。定义每个节点的Elder[i]为该节点的2^k(或者说是二进制中的1,10,100,1000...)辈祖先。求它时要利用性质:cur->Elder[i]==cur->Elder[i-1]->Elder[i-1]。具体步骤看代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; const int MAX_FA = 10, MAX_NODE = 500010, MAX_EDGE = MAX_NODE * 2; struct Node;
struct Edge; struct Node
{
int Id, Depth;
Edge *Head;
Node *Elder[MAX_FA];
}_nodes[MAX_NODE], *Root;
int TotNode; struct Edge
{
Node *From, *To;
Edge *Next;
}*_edges[MAX_EDGE];
int _edgeCnt; void Init(int root, int totNode)
{
_edgeCnt = 0;
TotNode = totNode;
Root = _nodes + root;
memset(_nodes, 0, sizeof(_nodes));
} Edge *NewEdge()
{
return _edges[++_edgeCnt] = new Edge();
} void AddEdge(Node *from, Node *to)
{
Edge *e = NewEdge();
e->From = from;
e->To = to;
e->Next = from->Head;
from->Head = e;
} void Build(int uId, int vId)
{
Node *u = _nodes + uId, *v = _nodes + vId;
u->Id = uId;
v->Id = vId;
AddEdge(u, v);
AddEdge(v, u);
} int Log2(int x)
{
int cnt = 0;
while (x / 2)
{
cnt++;
x /= 2;
}
return cnt;
} void Dfs(Node *cur, Edge *FromFa)
{
if(!FromFa)
cur->Depth = 1;
else
{
cur->Elder[0] = FromFa->From;
cur->Depth = cur->Elder[0]->Depth + 1;
for(int i=1; cur->Elder[i-1]->Elder[i-1]; i++)
cur->Elder[i] = cur->Elder[i-1]->Elder[i-1];
}
for(Edge *e = cur->Head; e; e=e->Next)
if(e->To!=cur->Elder[0])
Dfs(e->To, e);
} void DfsStart()
{
Dfs(Root, NULL);
} Node *Lca(Node *deep, Node *high)
{
if (deep->Depth < high->Depth)
swap(deep, high);
int len = deep->Depth - high->Depth;
for(int k=0; len; k++)
{
if((1 << k) & len)
{
deep=deep->Elder[k];
len -= (1 << k);//把len二进制中当前的1去掉
}
}
if (deep == high)
return deep;
for (int k = Log2(deep->Depth); k >= 0; k--)
{
if (deep->Elder[k] != high->Elder[k])
{
deep = deep->Elder[k];
high = high->Elder[k];
}
}
return deep->Elder[0];
} int main()
{
int totNode, totQ, rootId, uId, vId, id1, id2;
scanf("%d%d%d", &totNode, &totQ, &rootId);
Init(rootId, totNode);
for (int i = 1; i < totNode; i++)
{
scanf("%d%d", &uId, &vId);
Build(uId, vId);
}
DfsStart();
for (int i = 1; i <= totQ; i++)
{
scanf("%d%d", &id1, &id2);
printf("%d\n", Lca(id1 + _nodes, id2 + _nodes)->Id);
}
return 0;
}

  

对Lca中for循环正确性的解释:每个整数都可以表示为sum(2^k)。所以以此方式可以到达一个节点的任意辈祖先。

注意:

  • k初值有log。
  • 树的深度和高度要区分开来。
  • Dfs时一开始循环中的判断cur->[k-1]!=NULL是为了处理根节点。

luogu3379 【模板】最近公共祖先(LCA) 倍增法的更多相关文章

  1. 最近公共祖先 LCA 倍增法

    [简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...

  2. [模板] 最近公共祖先/lca

    简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...

  3. 【lhyaaa】最近公共祖先LCA——倍增!!!

    高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...

  4. 最近公共祖先 LCA 倍增算法

          树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...

  5. LCA(最近公共祖先)——LCA倍增法

    一.前人种树 博客:最近公共祖先 LCA 倍增法 博客:浅谈倍增法求LCA 二.沙场练兵 题目:POJ 1330 Nearest Common Ancestors 代码: const int MAXN ...

  6. POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)

    POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...

  7. 最近公共祖先(LCA)的三种求解方法

    转载来自:https://blog.andrewei.info/2015/10/08/e6-9c-80-e8-bf-91-e5-85-ac-e5-85-b1-e7-a5-96-e5-85-88lca- ...

  8. POJ - 1330 Nearest Common Ancestors(dfs+ST在线算法|LCA倍增法)

    1.输入树中的节点数N,输入树中的N-1条边.最后输入2个点,输出它们的最近公共祖先. 2.裸的最近公共祖先. 3. dfs+ST在线算法: /* LCA(POJ 1330) 在线算法 DFS+ST ...

  9. Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)

    Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...

随机推荐

  1. iOS学习笔记07-运动事件和远程控制

    之前我们已经学习了触摸处理和手势识别,其实这两个同属于iOS事件的触摸事件,今天我们来学习下iOS事件的另外两个事件: 一.运动事件 运动事件,是通过加速器进行触发,和触摸事件一样,继承UIRespo ...

  2. 【Luogu】P1005矩阵取数游戏(高精度+DP)

    题目链接 yeah终于过辣! DP,f[i][j]表示每行还剩i到j这个区间的数没取的时候的值.借这个题我也把高精度的短板弥补了一下,以后高精加高精乘应该是没问题了. 哇终于不怂高精了…… 放上代码. ...

  3. USACO 2.1 The Castle

    题目大意:给你一个城堡让你求有多少房间,最大房间有多大,敲掉一堵墙后最大的房间有多大,敲掉那座墙 思路:比较恶心的bfs题,反正就是bfs使劲敲 /*{ ID:a4298442 PROB:castle ...

  4. ORACLE无法删除当前连接用户

    今天在做Oracle数据库是遇到ORACLE无法删除当前连接用户 ,经查找可用如下方法解决 . 在Oracle中删除用户时提示:ORACLE无法删除当前连接用户  可以用以下语句    Sql代码   ...

  5. bzoj1734 [Usaco2005 feb]Aggressive cows 愤怒的牛 二分答案

    [Usaco2005 feb]Aggressive cows 愤怒的牛 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 407  Solved: 325[S ...

  6. 【HDOJ6222】Heron and His Triangle(Java,二分,递推)

    题意:让你找这样的一个三角形,三条边为t,t-1,t+1,并且面积为整数,最后满足t大于等于n. n<=1e30 思路:直接推式子不会,打表找规律 f(n)=4*f(n-1)-f(n-2)(n& ...

  7. nyoj_90_整数划分_201403161553

    整数划分 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 将正整数n表示成一系列正整数之和:n=n1+n2+…+nk, 其中n1≥n2≥…≥nk≥1,k≥1. 正整数 ...

  8. Object源码

    1.Object是所有类的父类,默认会继承Object. 2.Object类中常用的方法有:getClass().hashCode().equals().clone().toString().fina ...

  9. javaScript 翻转

    一个字符串转成如下形式: 一个字符串转成如下形式"olleh dlrow"; public class reverseWord { public static void main( ...

  10. 【Lucene】具体解释Lucene全文检索的信息写入与读取

    Lucene的大致结构图: 信息写入索引库的过程: 读取信息的过程: 以下是一个向索引库写入信息与读取信息的样例: public void testCreateIndex() throws Excep ...