来自:http://www.cnblogs.com/ylfdrib/archive/2010/11/03/1867901.html

对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。

0

|

1

/   \

2      3

比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点。

在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n + q),n为总结点数,q为询问结点对数。

另外Tarjan解法,是一个离线算法,就是说它必须将所有询问先记录下来,再一次性的求出每个点对的最近公共祖先,只有这样才可以达到降低时间复杂度。另外还有一个在线算法,有待学习。

伪代码:

//parent为并查集,FIND为并查集的查找操作
//QUERY为询问结点对集合
//TREE为基图有根树
Tarjan(u)
visit[u] = true
for each (u, v) in QUERY
if visit[v]
ans(u, v) = FIND(v)
for each (u, v) in TREE
if !visit[v]
Tarjan(v)
parent[v] = u

经典例题:

hdu2586 How far away ?

这道题题意是,给定一棵树,每条边都有一定的权值,q次询问,每次询问某两点间的距离。这样就可以用LCA来解,首先找到u, v 两点的lca,然后计算一下距离值就可以了。这里的计算方法是,记下根结点到任意一点的距离dis[],这样ans = dis[u] + dis[v] - 2 * dis[lca(v, v)]了,这个表达式还是比较容易理解的。。

//============================================================================
// Name : hdu2586.cpp
// Author : birdfly
// Description : 最近公共祖先
//============================================================================ #include <iostream>
#include <string.h>
#include <stdio.h>
#define NN 40002 // number of house
#define MM 202 // number of query
using namespace std; typedef struct node{
int v;
int d;
struct node *nxt;
}NODE; NODE *Link1[NN];
NODE edg1[NN * ]; // 树中的边 NODE *Link2[NN];
NODE edg2[NN * ]; // 询问的点对 int idx1, idx2, N, M;
int res[MM][]; // 记录结果,res[i][0]: u res[i][1]: v res[i][2]: lca(u, v)
int fat[NN];
int vis[NN];
int dis[NN]; void Add(int u, int v, int d, NODE edg[], NODE *Link[], int &idx){
edg[idx].v = v;
edg[idx].d = d;
edg[idx].nxt = Link[u];
Link[u] = edg + idx++; edg[idx].v = u;
edg[idx].d = d;
edg[idx].nxt = Link[v];
Link[v] = edg + idx++;
} int find(int x){ // 并查集路径压缩
if(x != fat[x]){
return fat[x] = find(fat[x]);
}
return x;
} void Tarjan(int u){
vis[u] = ;
fat[u] = u; for (NODE *p = Link2[u]; p; p = p->nxt){
if(vis[p->v]){
res[p->d][] = find(p->v); // 存的是最近公共祖先结点
}
} for (NODE *p = Link1[u]; p; p = p->nxt){
if(!vis[p->v]){
dis[p->v] = dis[u] + p->d;
Tarjan(p->v);
fat[p->v] = u;
}
}
}
int main() {
int T, i, u, v, d;
scanf("%d", &T);
while(T--){
scanf("%d%d", &N, &M); idx1 = ;
memset(Link1, , sizeof(Link1));
for (i = ; i < N; i++){
scanf("%d%d%d", &u, &v, &d);
Add(u, v, d, edg1, Link1, idx1);
} idx2 = ;
memset(Link2, , sizeof(Link2));
for (i = ; i <= M; i++){
scanf("%d%d", &u, &v);
Add(u, v, i, edg2, Link2, idx2);
res[i][] = u;
res[i][] = v;
} memset(vis, , sizeof(vis));
dis[] = ;
Tarjan(); for (i = ; i <= M; i++){
printf("%d\n", dis[res[i][]] + dis[res[i][]] - * dis[res[i][]]);
}
}
return ;
}

最新文章

  1. 浅谈Android应用保护(一):Android应用逆向的基本方法
  2. Android自定义控件----RadioGroup实现APP首页底部Tab的切换
  3. nginx实时记录请求状态信息( ngx_realtime_request_module)
  4. 贝叶斯决策_bayes(新闻分类)
  5. 微信移动客户端内部浏览器分享到朋友圈,QQ空间代码
  6. json中换行问题
  7. css中的hover ,关于li与a标签的问题
  8. .NET技术+25台服务器怎样支撑世界第54大网站
  9. 《并行程序设计导论》——OpenMP
  10. oracle数据导出以及导入
  11. 【容斥+组合数】Massage @2018acm徐州邀请赛 E
  12. 各种浏览器兼容trim()的方法
  13. Java连接postgreSQL数据库,找不到表。
  14. iOS.AutoLayout.2.CustomView-with-AutoLayout
  15. VisualSVN server 搭建SVN服务器
  16. OpenStack API部分高可用配置(二)
  17. tomcat 启动超级慢
  18. mysql数据库不能远程访问的问题
  19. Linux系统——NFS网络文件系统
  20. JSP和JS的区别

热门文章

  1. Proxool抛出的警告 was active for 365172 milliseconds and has been removed automaticaly
  2. C# 文字转成声音
  3. Linux下的lds链接脚本详解
  4. golang里面检测对象是否实现了接口的方法
  5. linux下使用tar命令详解
  6. 《Java核心技术》 -- 读书笔记 ① - 预热
  7. css 定位position总结
  8. JavaScript实现排序算法总结
  9. JavaScript中的可枚举属性与不可枚举属性
  10. Java规则引擎及JSR-94[转]