luogu3379 【模板】最近公共祖先(LCA) 倍增法
题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
整体步骤: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) 倍增法的更多相关文章
- 最近公共祖先 LCA 倍增法
[简介] 解决LCA问题的倍增法是一种基于倍增思想的在线算法. [原理] 原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现. 对于每个节点u , ancestors[u] ...
- [模板] 最近公共祖先/lca
简介 最近公共祖先 \(lca(a,b)\) 指的是a到根的路径和b到n的路径的深度最大的公共点. 定理. 以 \(r\) 为根的树上的路径 \((a,b) = (r,a) + (r,b) - 2 * ...
- 【lhyaaa】最近公共祖先LCA——倍增!!!
高级的算法——倍增!!! 根据LCA的定义,我们可以知道假如有两个节点x和y,则LCA(x,y)是 x 到根的路 径与 y 到根的路径的交汇点,同时也是 x 和 y 之间所有路径中深度最小的节 点,所 ...
- 最近公共祖先 LCA 倍增算法
树上倍增求LCA LCA指的是最近公共祖先(Least Common Ancestors),如下图所示: 4和5的LCA就是2 那怎么求呢?最粗暴的方法就是先dfs一次,处理出每个点的深度 ...
- LCA(最近公共祖先)——LCA倍增法
一.前人种树 博客:最近公共祖先 LCA 倍增法 博客:浅谈倍增法求LCA 二.沙场练兵 题目:POJ 1330 Nearest Common Ancestors 代码: const int MAXN ...
- POJ 1470 Closest Common Ancestors(最近公共祖先 LCA)
POJ 1470 Closest Common Ancestors(最近公共祖先 LCA) Description Write a program that takes as input a root ...
- 最近公共祖先(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- ...
- POJ - 1330 Nearest Common Ancestors(dfs+ST在线算法|LCA倍增法)
1.输入树中的节点数N,输入树中的N-1条边.最后输入2个点,输出它们的最近公共祖先. 2.裸的最近公共祖先. 3. dfs+ST在线算法: /* LCA(POJ 1330) 在线算法 DFS+ST ...
- Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集)
Luogu 2245 星际导航(最小生成树,最近公共祖先LCA,并查集) Description sideman做好了回到Gliese 星球的硬件准备,但是sideman的导航系统还没有完全设计好.为 ...
随机推荐
- BZOJ 1007 [HNOI2008]水平可见直线 ——半平面交 凸包
发现需要求一个下凸的半平面上有几个交点. 然后我们把它变成凸包的问题. 好写.好调.还没有精度误差. #include <map> #include <ctime> #incl ...
- uva 11798 相对运动的最小最大距离
C Dog Distance Input Standard Input Output Standard Output Two dogs, Ranga and Banga, are running ra ...
- SHoj A序列
A序列 发布时间: 2017年7月9日 18:17 最后更新: 2017年7月9日 21:05 时间限制: 1000ms 内存限制: 128M 描述 如果一个序列有奇数个正整数组成,不妨令 ...
- Find that single one.(linear runtime complexity0
public class Solution { public int singleNumber(int[] nums) { int temp = 0; for (int i=0;i<nums.l ...
- MongoDB_语法命令
可以通过MongoDB shell 来连接MongoDB服务: ./mongo 进入交互 数据库-->集合-->文档 几个文档就组成了集合,可以设置固定大小的集合,集合就会有过期机制, ...
- Perl语言入门--1--perl的包安装
A. 手工安装 从CPAN下载了DBI模块1.13版的压缩文件DBI-1.13.tar.gz.假设放在/usr/local/src(一般需要编译安装的软件,都放在此目录下安装). # cd /usr ...
- Scrapy学习-1-入门
基础知识 爬虫发展史 爬虫去重 1. 存储到数据库中 存取速度慢 2. 存储到内存中的集合里,内存占用十分大 当爬取数据有1亿条时 1*10**8*2Byte*50str_len/1024/102 ...
- php——验证身份证是否合法的函数
function is_idcard( $id ){ $id = strtoupper($id); $regx = "/(^\d{15}$)|(^\d{17}([0-9]|X)$)/&quo ...
- hdu2448 / 费用流 / harbin赛区c题
题(自)目(己)错(英)综(语)复(太)杂(差),关系理了半小时+翻译才看明白,看明白之后,直接建图,费用流击杀./简单题. 2A:有的地方,可用互通的要建双向边! #include<cstdi ...
- Codeforces 515E Drazil and Park (ST表)
题目链接 Drazil and Park 中文题面 传送门 如果他选择了x和y,那么他消耗的能量为dx + dx + 1 + ... + dy - 1 + 2 * (hx + hy). 把这个式子写成 ...