这么久了才做LCA的题,以前是感觉很难不敢去尝试,现在学习了一番之后发现算法本身并不难。。。。

  学习时看了这篇博文:https://www.cnblogs.com/JVxie/p/4854719.html, 我觉得实现的过程最重要,就把博文中Tarjan算法实现的方法以及伪代码贴到下面:

Tarjan算法的基本思路:

      1.任选一个点为根节点,从根节点开始。

      2.遍历该点u所有子节点v,并标记这些子节点v已被访问过。

      3.若是v还有子节点,返回2,否则下一步。

      4.合并v到u上。

      5.寻找与当前点u有询问关系的点v。

      6.若是v已经被访问过了,则可以确认u和v的最近公共祖先为v被合并到的父亲节点a。

    遍历的话需要用到dfs来遍历,至于合并,最优化的方式就是利用并查集来合并两个节点。

    下面上伪代码:

Tarjan(u)//marge和find为并查集合并函数和查找函数
{
for each(u,v) //访问所有u子节点v
{
Tarjan(v); //继续往下遍历
marge(u,v); //合并v到u上
标记v被访问过;
}
for each(u,e) //访问所有和u有询问关系的e
{
如果e被访问过;
u,e的最近公共祖先为find(e);
}
}

    算法的运用以及实现过程的举例,博文中讲的很详细,我想补充一下我对Tarjan算法的理解,

  当程序在dfs过程中遍历到某个节点 N 时, 以 N 节点为根节点搜索它的子节点,在它的某一子节点或某一子树都遍历过后,遍历过的点的父节点都会变成节点 N。那么假设N的子树中 i 节点被遍历了,其父节点变成N ; 遍历到其他子节点 j 时, 若恰好存在询问 i , j ,且 i , j 都在以 N 为根的树内,那么就可以直接得到 i, j 的LCA为 N 节点。那么在一次dfs过程中就都得到了所有询问的LCA

以下是两道例题的AC代码:

CODEVS 2370 小机房的树

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int MAXN=;
struct Edge{
int to;
int cost;
int id;
};
vector<Edge> vec[MAXN];
vector<Edge> Q[MAXN];
bool vis[MAXN];
int p[MAXN], res[MAXN];
long long d[MAXN], resd[MAXN];
void add_edge(int u, int v, int c){
vec[u].push_back((Edge){v, c, -});
vec[v].push_back((Edge){u, c, -});
}
void add_query(int a, int b, int id){
Q[a].push_back((Edge){b, , id});
Q[b].push_back((Edge){a, , id});
}
int find(int x){
return (p[x]==x)?x:(p[x]=find(p[x]));
}
void Union(int x, int y)
{
x=find(x);
y=find(y);
if(x==y) return;
else p[x]=y;
}
void tarjan(int now, int fa)
{
for(int i=;i<vec[now].size();i++){
Edge e=vec[now][i];
if(!vis[e.to]&&e.to!=fa){
/**/
d[e.to]=d[now]+e.cost;
/**/
tarjan(e.to, now);
Union(e.to, now);//注意now 和 e.to 的顺序 }
}
for(int i=;i<Q[now].size();i++){
Edge e=Q[now][i];
if(vis[e.to]){
res[e.id]=find(e.to);
resd[e.id]=d[e.to]+d[now]-*d[res[e.id]];
}
}
vis[now]=;
}
int main()
{
int n,m,a,b,c;
while(~scanf("%d", &n))
{
for(int i=;i<=n;i++) p[i]=i;
for(int i=;i<n-;i++){
scanf("%d %d %d", &a, &b, &c);
add_edge(a, b, c);
}
scanf("%d", &m);
for(int i=;i<m;i++){
scanf("%d %d", &a, &b);
add_query(a, b, i);
} memset(vis, , sizeof(vis));
memset(d, , sizeof(d));
tarjan(, -);
for(int i=;i<m;i++){
printf("%lld\n", resd[i]);
}
}
}
/*
9
1 2 0
1 3 0
2 4 0
2 5 0
3 6 0
5 7 0
5 8 0
7 9 0
4
9 8
4 6
7 5
5 3
*/

CODEVS 1036 商务旅行

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
using namespace std;
const int MAXN=;
struct Edge{
int to;
int cost;
int id;
};
vector<Edge> vec[MAXN];
vector<Edge> Q[MAXN];
bool vis[MAXN];
int p[MAXN], res[MAXN];
long long d[MAXN], resd[MAXN];
void add_edge(int u, int v, int c){
vec[u].push_back((Edge){v, c, -});
vec[v].push_back((Edge){u, c, -});
}
void add_query(int a, int b, int id){
Q[a].push_back((Edge){b, , id});
Q[b].push_back((Edge){a, , id});
}
int find(int x){
return (p[x]==x)?x:(p[x]=find(p[x]));
}
void Union(int x, int y)
{
x=find(x);
y=find(y);
if(x==y) return;
else p[x]=y;
}
void tarjan(int now, int fa)
{
for(int i=;i<vec[now].size();i++){
Edge e=vec[now][i];
if(!vis[e.to]&&e.to!=fa){
/**/
d[e.to]=d[now]+e.cost;
/**/
tarjan(e.to, now);
Union(e.to, now);//注意now 和 e.to 的顺序 }
}
for(int i=;i<Q[now].size();i++){
Edge e=Q[now][i];
if(vis[e.to]){
res[e.id]=find(e.to);
resd[e.id]=d[e.to]+d[now]-*d[res[e.id]];
}
}
vis[now]=;
}
int main()
{
int n,m,a,b,c;
while(~scanf("%d", &n))
{
for(int i=;i<=n;i++) p[i]=i;
for(int i=;i<n-;i++){
scanf("%d %d", &a, &b);
add_edge(a, b, );
}
scanf("%d", &m);
scanf("%d", &a);
for(int i=;i<m;i++){
scanf("%d", &b);
add_query(a, b, i);
a=b;
} memset(vis, , sizeof(vis));
memset(d, , sizeof(d));
tarjan(, -);
long long ans=;
for(int i=;i<m;i++){
ans+=resd[i];
}
printf("%lld\n", ans);
}
}

简单的LCA的更多相关文章

  1. poj 1330 Nearest Common Ancestors (最简单的LCA)

    题意: 给出一棵树的结构. 给出两个点X和Y,求它俩的LCA. 思路: 只需求两个点的LCA,用了两种方法,一种离线tarjan,一种直接搞. 看代码. 代码: 方法一:直接搞. int const ...

  2. [BZOJ1602&BZOJ1787&BZOJ2144]树上LCA的算法巩固练习

    简述求LCA的倍增算法 对于树上的所有节点,我们可以很轻松地通过dfs求出其直接的父亲节点以及其深度 通过类似RMQ的原理我们可以处理出每个节点的第2^i个父亲 //这个过程既可以在dfs之后双重循环 ...

  3. HDU 5266 pog loves szh III 线段树,lca

    Pog and Szh are playing games. Firstly Pog draw a tree on the paper. Here we define 1 as the root of ...

  4. poj2月题解

    竟然生日前一天poj破百,不错不错,加速前进! poj2437 由于泥泞不重叠,所以按其实左边排个序再统计一遍即可(如果不是刚好盖满就尽量往后盖) poj2435 细节bfs poj2230 求欧拉回 ...

  5. P1536 村村通(洛谷)并查集

    隔壁的dgdger带我看了看老师的LCA教程,我因为学习数学太累了(就是懒),去水了一下,感觉很简单的样子,于是我也来写(水)个博客吧. 题目描述 某市调查城镇交通状况,得到现有城镇道路统计表.表中列 ...

  6. 2249: Altruistic Amphibians 01背包的应用 + lh的简单图论 图转树求lca

    第一个 写了两个比较简单的数论题目,就是整除理论的两个题目,第一个题目比较蠢,第二个稍微要动一点脑筋 Codeforces Round #347 (Div. 2) – A. Complicated G ...

  7. poj----1330Nearest Common Ancestors(简单LCA)

    题目连接  http://poj.org/problem?id=1330 就是构建一棵树,然后问你两个节点之间最近的公共父节点是谁? 代码: /*Source Code Problem: 1330 U ...

  8. POJ 2763 Housewife Wind 纯粹LCA写法(简单无脑)

    Description After their royal wedding, Jiajia and Wind hid away in XX Village, to enjoy their ordina ...

  9. (RMQ版)LCA注意要点

    inline int lca(int x,int y){ if(x>y) swap(x,y); ]][x]]<h[rmq[log[y-x+]][y-near[y-x+]+]])? rmq[ ...

随机推荐

  1. Canvas入门03-绘制弧线和圆

    绘制弧线的API: context.arc(centerx:number, centery: number, radius: number, startAngle: number, endAngle: ...

  2. docker--搭建docker swarm集群

    10 搭建docker swarm集群 10.1 swarm 介绍 Swarm这个项目名称特别贴切.在Wiki的解释中,Swarm behavior是指动物的群集行 为.比如我们常见的蜂群,鱼群,秋天 ...

  3. 1~n的全排列--阅文集团2018校招笔试题

    题目大意:给定整数n,求出1~n的全排列 示例 输入:n=3 输出:[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] import java.util.S ...

  4. sqlplus无法登陆?

    关键词:error 6 initialize sqlplus,ORA-27101: shared memory realm does not exist 1.error 6 initialize sq ...

  5. Android怎么改图标都不生效&&Android studio 如何修改APP图标和名字

    去这里(我自己写的),解决方法包你满意: https://blog.csdn.net/qq_43141611/article/details/101875545

  6. MyBatis二级缓存的笔记及记录

    一.什么是二级缓存: 由于一级缓存是一次性的.临时的:每个会话都会创建一个新的:多个会话之间是不能共享的: 二级缓存用于解决一级缓存的不足:每一个“namespace”都会对应一个二级缓存:执行查询的 ...

  7. 【JZOJ 3918】蛋糕

    题面: 正文: 根据数据\(4\leq R,C\leq 75\)我们大概可以先枚举切横的刀再二分答案. 更具体的: 假设我们已经枚举到这样横切: 再假设我们已经二分到最小的巧克力是\(7\) 康康第一 ...

  8. ThinkPHP5 支付宝支付扩展库(超级简单,超级好用!)

    ThinkPHP5 支付宝支付扩展库, 一个静态方法的调用就可以实现,包括手机网站支付.电脑网站支付.支付查询.退款.退款查询.对账单所有功能,而且是2017年7月20日最新版~我的想法是,调用一个静 ...

  9. 深度学习之group convolution,计算量及参数量

    目录: 1.什么是group convolution? 和普通的卷积有什么区别? 2.分析计算量.flops 3.分析参数量 4.相比于传统普通卷积有什么优势以及缺点,有什么改进方法? 5.refer ...

  10. Set中如何区分重复元素

    Set接口常用实现类:HashSet和TreeSet HashSet区分重复元素: 先使用hashcode方法判断已经存在HashSet中元素的hashcode值和将要加入元素hashcode值是否相 ...