这么久了才做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. Django-DRF组件学习-其他学习

    1.认证Authentication 可以在配置文件中配置全局默认的认证方案 REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_ ...

  2. 【HBase】五、HBase的Java接口

      HBase是Hadoop中的一个重要组件,自然也是基于Java语言开发的,因此HBase有很好的Java接口供程序员调用,通过一个例子来演示java如何使用HBase数据库.   要想在HBase ...

  3. vue--》如何使用wacth监听对象的属性变化?

    在开发过程中,我们经常需要监听watch监听一个对象的变化,但是如何来实现     监听对象中属性的变化呢? 先回顾一下如何监听整个对象的变化,使用watch就行了 export default { ...

  4. if——while表达式详解

    ①while循环的表达式是循环进行的条件,用作循环条件的表达式中一般至少包括一个能够改变表达式的变量,这个变量称为循环变量 ②当表达式的值为真(非零)(非空)时,执行循环体:为假(0)时,则循环结束 ...

  5. 解析xml的4种方法详解(转)

    http://blog.csdn.net/jzhf2012/article/details/8532873 1. 介绍 1)DOM(JAXP Crimson解析器)         DOM是用与平台和 ...

  6. get与post请求的区别 (面试会问)

    get和post请求是HTTP与服务器交互方式,也就是通常所说的风别对服务器资源的增删改查 1. post是修改数据   get是获得数据 GET在浏览器回退时是无害的,而POST会再次提交请求.(面 ...

  7. IntelliJ IDLE

    目录 IntelliJ IDLE java项目结构 代码生成 psvm sout 快捷键 更改代码提示快捷键 代码相关快捷键 窗口相关快捷键 Debug快捷键 主题导入 下载主题 导入主题 应用主题 ...

  8. [LeetCode] 203. 移除链表元素

    题目链接:https://leetcode-cn.com/problems/remove-linked-list-elements/ 题目描述: 删除链表中等于给定值 val 的所有节点. 示例: 输 ...

  9. Redis---使用场景

    3.使用场景 计数器   可以对String进行自增自减运算,从而实现计算器功能.   Redis这种内存型数据库的读写性能非常高,很适合存储频繁读写的及数量 缓存   将热点数据放到内存中,设置内存 ...

  10. 为ASP.NET按钮(Button)添加确认对话框

    http://www.cnblogs.com/blodfox777/articles/1261303.html Button有两个点击事件: onclick 触发服务端事件,脚本为c#或VB.NET ...