最近公共祖先 LCA 倍增法
【简介】
解决LCA问题的倍增法是一种基于倍增思想的在线算法。
【原理】
原理和同样是使用倍增思想的RMQ-ST 算法类似,比较简单,想清楚后很容易实现。
对于每个节点u , ancestors[u][k] 表示 u 的第2k个祖先是谁。很容易就想到递推式: ancestors[j][i] = ancestors[ancestors[j][i - 1]][i - 1]; 根据二进制原理,理论上 u 的所有祖先都可以根据ancestors数组多次跳转得到,这样就间接地记录了每个节点的祖先信息。
查询LCA(u,v)的时候:
(一)u和v所在的树的层数如果一样,令u'=u。否则需要平衡操作(假设u更深),先找到u的一个祖先u', 使得u'的层数和v一样,此时LCA(u,v)=LCA(u',v) 。证明很简单:如果LCA(u,v)=v , 那么u'一定等于v ;如果LCA(u,v)=k ,k!=v ,那么k 的深度一定小于 v , u、u'、v 一定在k的子树中;综上所述,LCA(u,v)=LCA(u',v)一定成立。
(二)此时u' 和 v 的祖先序列中一开始的部分一定有所重叠,重叠部分的最后一个元素(也就是深度最深,与u'、v最近的元素)就是所求的LCA(u,v)。这里ancestors数组就可以派上用场了。找到第一个不重叠的节点k,LCA(u,v)=ancestors[k][0] 。 找k的过程利用二进制贪心思想,先尽可能跳到最上层的祖先,如果两祖先相等,说明完全可以跳小点,跳的距离除2,这样一步步跳下去一定可以找到k。
【hdu 2586】
需要注意的是超界的处理。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
int n,m;
struct edge
{
int d,v,next;
edge(){}
edge(int _d,int _v,int _next)
{
d=_d;v=_v;next=_next;
}
}data[];
int map[];
int pool;
void addedge(int s,int e,int v)
{
int t=map[s];
data[pool++]=edge(e,v,t);
map[s]=pool-;
}
int ANCLOG;
int depth[];
int ifv[];
int dis[];
int anc[][];
void dfs(int cur,int dep)
{
ifv[cur]=;
depth[cur]=dep;
int p=map[cur];
while (p!=-)
{
if (!ifv[data[p].d])
{
dis[data[p].d]=dis[cur]+data[p].v;
anc[data[p].d][]=cur;
dfs(data[p].d,dep+);
}
p=data[p].next;
}
}
void initLCA()
{
for (int k=;k<ANCLOG;++k)
for (int i=;i<n;++i)
{
if (anc[i][k-]==-) continue;
anc[i][k]=anc[anc[i][k-]][k-];
}
}
int getLCA(int u,int v)
{
if (depth[u]<depth[v]) swap(u,v);
for (int k=ANCLOG;k>=;--k)
{
if (anc[u][k]==-) continue;
if (depth[anc[u][k]]>=depth[v])
{
u=anc[u][k];
if (depth[u]==depth[v]) break;
}
}
if (u==v) return u;
for (int k=ANCLOG;k>=;--k)
{
if (anc[u][k]==-) continue;
if (anc[u][k]!=anc[v][k])
{
u=anc[u][k];
v=anc[v][k];
}
}
return anc[u][];
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
pool=;
memset(anc,-,sizeof anc);
memset(map,-,sizeof map);
memset(ifv,,sizeof ifv);
scanf("%d%d",&n,&m);
ANCLOG=(int)(log(n)/log(2.0));
int s,e,v;
for (int i=;i<n-;++i)
{
scanf("%d%d%d",&s,&e,&v);
addedge(s-,e-,v);
addedge(e-,s-,v);
}
dis[]=;
dfs(,);
initLCA();
for (int i=;i<m;++i)
{
int u,v;
scanf("%d%d",&u,&v);
--u;--v;
int k=getLCA(u,v);
k=dis[u]+dis[v]-*dis[k];
printf("%d\n",k);
}
}
}
最近公共祖先 LCA 倍增法的更多相关文章
- luogu3379 【模板】最近公共祖先(LCA) 倍增法
题目大意:给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先. 整体步骤:1.使两个点深度相同:2.使两个点相同. 这两个步骤都可用倍增法进行优化.定义每个节点的Elder[i]为该节点的2^k( ...
- 【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的导航系统还没有完全设计好.为 ...
- POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA)
POJ 1330 Nearest Common Ancestors / UVALive 2525 Nearest Common Ancestors (最近公共祖先LCA) Description A ...
随机推荐
- Part2-HttpClient官方教程-Chapter2-连接管理
2.1 连接持久性 建立从一个主机到另一个主机的连接的过程相当复杂,并且涉及两个端点之间的多个分组交换,这可能相当耗时.连接握手的开销可能很大,特别是对于小型的HTTP消息. 如果可以重新使用开放连接 ...
- LeetCode 19 Valid Parentheses
Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...
- printk %pF %pS含义【转】
作者:啐楼链接:https://www.zhihu.com/question/37769890/answer/73532192来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...
- USB 3.1 與 USB Type-C 解釋
https://tw.transcend-info.com/Support/FAQ-940 以下的內容皆來自上面這個網址. 什麼是USB 3.1? 什麼是USB 3.1? USB 3.1為USB協會制 ...
- 用selenium 模块控制浏览器
11.8 用selenium 模块控制浏览器selenium 模块让Python 直接控制浏览器,实际点击链接,填写登录信息,几乎就像是有一个人类用户在与页面交互.与Requests 和Beautif ...
- 查看mysql的版本和端口号
查看版本:select version(); 查看端口号:show global variables like 'port';
- 应用程序有bug崩溃重启的案例2
------解决思路----------------------另外做一个服务或者程序定时监控系统进程.程序奔溃的话,都会在入口函数出现异常处理一下winform可以有两个事件来捕获主线程异常和线程异 ...
- AC日记——背单词 洛谷 P2353
背单词 思路: KMP+统计前缀和优化: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 1000005 ], ...
- yum 安装
可以有两种方式:1.sudo yum install 然后输入root密码2.su root,输入密码然后yum install
- coercing to Unicode错误的一个解决办法
http://blog.csdn.net/happen23/article/details/46683813