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

整体步骤: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. Hibernate批量更新和批量删除批量添加(转)

    通常,在一个Session对象的缓存中只存放数量有限的持久化对象,等到Session对象处理事务完毕,还要关闭Session对象,从而及时释放Session的缓存占用的内存.批量处理数据是指在一个事务 ...

  2. java system.out.printf()的使用方法

    package test; public class Main { public static void main(String[] args) { // 定义一些变量,用来格式化输出. double ...

  3. 使用sudo,mvn command not found

    一个简单的解决办法是,编辑你当前用户的 .bashrc 文件,添加下面这行内容: alias sudo="sudo env PATH=$PATH" 因为系统预装的 sudo 在编译 ...

  4. Spring的@Configuration来代替xml配置

    一. Xml配置法 下面是一个典型的spring配置文件(application-config.xml): [xml] view plain copy <beans> <bean i ...

  5. 从dataset表中获取某一列的所有值方法

    原文发布时间为:2008-07-31 -- 来源于本人的百度文章 [由搬家工具导入] 可以datarow遍历所有行即可,如下:pubauthor这个表中的au_lname的所有值加到listbox上面 ...

  6. C++函数传递指向指针的指针的应用

    传递指向指针的引用假设我们想编写一个与前面交换两个整数的 swap 类似的函数,实现两个指针的交换.已知需用 * 定义指针,用 & 定义引用.现在,问题在于如何将这两个操作符结合起来以获得指向 ...

  7. Android 网络通信之Socket

    Android 网络通信之Socket 应用软件的网络通信无非就是Socket和HTTP,其中Socket又可以用TCP和UDP,HTTP的话就衍生出很多方式,基础的HTTP GET和POST请求,然 ...

  8. centos tomcat 关于日志

    一.实时查看tomcat的日志 1.先切换到tomcat5/logs 2.tail -f catalina.out 3.这样运行时就可以实时查看运行日志了 例如: cd /tomcat7/logs t ...

  9. IntelliJ IDEA配置Tomcat/Jetty运行Web项目

    一.使用Maven的POM引入插件的形式: 这种方式只需在POM中引入Tomcat/Jetty的插件即可运行.参考:http://www.cnblogs.com/EasonJim/p/6687272. ...

  10. Spring的Bean定义

    以下内容引用自http://wiki.jikexueyuan.com/project/spring/bean-definition.html: Bean定义 被称作bean的对象是构成应用程序的支柱也 ...