LCA入门题。所谓LCA就是最近公共祖先,题目就是讲这个意思。题意:假如a的祖先节点有d,e,f;b的祖先节点有c,e,f。那么e和f就是a和b的公共祖先,如果e比f离a和b比较近,那么e就是a和b的最近公共祖先。另外,节点本身也算是本身的祖先节点,所以假设a的父节点是b,那么a和b的最近公共祖先是b。现在根据这个定义,输入一颗有根树,最后有一个询问,请输出询问中两个节点的最近公共祖先也就是LCA。
我的解题思路:关于求LCA有一个Tarjan的离线算法,原理是从根节点开始DFS借助幷查集可以求出任意两点的最近公共祖先。下面给出解题代码,在代码中详细解释。
我的解题代码:离线的算法
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
#define N 10001
bool root[N]; //判断是否为根节点
bool vis[N];
int father[N]; //幷查集使用
vector <int> e[N]; //存储子节点
vector <int> q[N]; //存储询问,不过本题是单一询问
int lca[N]; //存储最近公共祖先
int t, n;
void InitRead();
int Find(int x);
void DataProcess(int x);
int main()
{
scanf("%d", &t);
while (t--)
{
InitRead();
for (int i=1; i<=n; ++i)
{
if (root[i])
{
DataProcess(i);
break;
}
}
}
return 0;
}
void InitRead()
{
scanf("%d", &n);
for (int i=0; i<=n; ++i)
{
e[i].clear();
q[i].clear();
root[i] = true;
vis[i] = false;
father[i] = i;
}
int a, b;
for (int i=1; i<n; ++i)
{
scanf("%d %d", &a, &b);
e[a].push_back(b);
root[b] = false;
}
scanf("%d %d", &a, &b);
q[a].push_back(b);
q[b].push_back(a);
return;
}
int Find(int x)
{
int z, y = x;
while (y != father[y]) y = father[y];
while (x != father[x])
{
z = father[x];
father[x] = y;
x = z;
}
return y;
}
void DataProcess(int x)
{
lca[x] = x;
int size = e[x].size();
for (int i=0; i<size; ++i)
{
DataProcess(e[x][i]); //求e[x][i]及其子节点等的LCA
father[e[x][i]] = x; //合并,但是我们不需要写专门的合并函数了
}
vis[x] = true;
size = q[x].size();
for (int i=0; i<size; ++i)
{
//输出x与q[x][i]的lca
if (vis[q[x][i]]) printf("%d\n", lca[Find(q[x][i])]);
return;
}
return;
}