对于最近公共祖先问题,我们先来看这样一个性质,当两个节点(u,v)的最近公共祖先是x时,那么我们可以确定的说,当进行后序遍历的时候,必然先访问完x的所有子树,然后才会返回到x所在的节点。这个性质就是我们使用Tarjan算法解决最近公共祖先问题的核心思想。
同时我们会想这个怎么能够保证是最近的公共祖先呢?我们这样看,因为我们是逐渐向上回溯的,所以我们每次访问完某个节点x的一棵子树,我们就将该子树所有节点放进该节点x所在的集合,并且我们设置这个集合所有元素的祖先是该节点x。那么我们有理由相信, 任何一个不属于已经访问的节点和已经访问的节点的LCA一定是当前这个根节点. 于是我们每次访问完一棵子树, 只需要将子树放进根节点对应的集合即可.
对于一棵子树所有节点,祖先都是该子树的根节点,所以我们在回溯的时候,时常要更新整个子树的祖先,为了方便处理,我们使用并查集维护一个集合的祖先。总的时间复杂度是O(n+q)的,因为dfs是O(n)的,然后对于询问的处理大概就是O(q)的。
以上摘自http://blog.csdn.net/geniusluzh/article/details/6609685
以下是整体代码:
#include <cstdio>
#include <cstring>
const int MAXNODE = 40010;
const int MAXEDGE = 80010;
typedef int Type;
const Type INF = 0x3f3f3f3f; //1e20
//存放边
struct Edge{
int u, v, next;
Type dis;
Edge() {}
Edge(int u, int v, int next, Type dis): u(u), v(v), next(next), dis(dis) {}
};
//存放问题,ok标记指的是这个问题被回答了没有
struct Question{
int u, v, next;
bool ok;
Question() {}
Question(int u, int v, int next): u(u), v(v), next(next), ok(false){}
};
struct Tarjan{
Edge edges[MAXEDGE];
Question ques[MAXNODE];
int n, m, q;
//用邻接表存储问题和边,LCA指的是第几个问题的最近公共祖先,f数组用来记录祖先结点
int EdgeHead[MAXNODE], f[MAXNODE], LCA[MAXNODE], QuesHead[MAXNODE];
bool vis[MAXNODE];
Type dis[MAXNODE];
void init(int n) {
this->n = n;
m = q = 0;
memset(vis, 0, sizeof(vis));
memset(EdgeHead, -1, sizeof(EdgeHead));
memset(QuesHead, -1, sizeof(QuesHead));
memset(LCA, 0, sizeof(LCA));
}
//添加的应该是双向边
void AddEdge(int u, int v, Type dis) {
edges[m] = Edge(u, v, EdgeHead[u], dis);
EdgeHead[u] = m++;
}
//添加的应该是双向边
void AddQues(int x, int y) {
ques[q] = Question(x, y, QuesHead[x]);
QuesHead[x] = q++;
}
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
//tarjan,遍历u结点的所有结点,并将u结点的所有直系子节点的父亲设为u,再进行查询
void dfs(int u) {
f[u] = u;
vis[u] = true;
for (int i = EdgeHead[u]; ~i; i = edges[i].next) {
int v = edges[i].v;
if (!vis[v]) {
dis[v] = dis[u] + edges[i].dis;
dfs(v);
f[v] = u;
}
}
//查询,如果其中一个结点刚好是u,且另一个结点已经遍历过,则u和v的LCA就是f[v]
for (int i = QuesHead[u]; ~i; i = ques[i].next) {
//如果已经被询问过了
if (ques[i].ok) continue;
int v = ques[i].v;
if (vis[v]) {
LCA[i] = find(v);
ques[i].ok = ques[i ^ 1].ok = true;
}
}
}
}tarjan;
int main() {
return 0;
}