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

整体步骤: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. [BZOJ1592] [Usaco2008 Feb]Making the Grade 路面修整(DP)

    传送门 有个结论,每一个位置修改高度后的数,一定是原来在这个数列中出现过的数 因为最终结果要么不递增要么不递减, 不递增的话, 如果x1 >= x2那么不用动,如果x1 < x2,把x1变 ...

  2. 巴蜀2904 MMT数

    Description FF博士最近在研究MMT数. 如果对于一个数n,存在gcd(n,x)<>1并且n mod x<>0 那么x叫做n的MMT数,显然这样的数可以有无限个. ...

  3. angular中的this指向问题

    this是指向当前$scope的. 例如在ng-click的使用中,this是指向当前的$scope而并不是dom元素的. 我们可以使用this的一些方法和属性 我们打印一下this就会发现,this ...

  4. Mongodb报错:ERROR: child process failed, exited with error number 1

    Mongodb在启动时报错: 2018-10-16T11:18:54.533+0800 I CONTROL [main] Automatically disabling TLS 1.0, to for ...

  5. Yii 之视图数据块

    控制器代码: public $layout = 'common'; public function actionStudent(){ $data = array('page_name'=>'St ...

  6. [转]JVM 堆内存设置原理

    堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...

  7. javafx中多场景的切换

    0.前言 前段时间在做javafx的应用程序,遇到一些坑.以本文记录之.(如有更好的解决办法欢迎评论,本人小白,轻喷) 1.问题 按照官方的中文文档,成功的运行了单一界面的表单登录.于是想自己试试多界 ...

  8. android 环境变量配置,以及sdcard配置

    第一步,打开环境变量配置窗口.右击计算机,属性-高级系统设置-环境变量. 第二步,添加android系统环境变量.在系统变量下,选择path,点击编辑,然后在最前面输入android sdk开发工具, ...

  9. 【深入Java虚拟机】之六:Java语法糖

    语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家Peter.J.Landin发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使 ...

  10. Win10激活Office2013的技巧

    原文:http://www.xitongzhijia.net/xtjc/20150720/53252.html KMSpico Win10激活工具 是一款能激活Win8/Win8.1/win10/Of ...