LCA即最小公共祖先,tarjan为LCA的离线算法, 可通过dfs和并查集实现。
算法核心思想是用dfs遍历整个树,在遍历到底层返回过程中将当前节点的祖先设为其父亲节点,并遍历需要求解的两点中以当前节点为起点的另外一点是否已访问过,若已访问过,则该答案就是另外一节点当前的祖先。具体看代码注释。
核心代码:
void tarjan(int u)
{
vis[u] = true;//标记
for(int i = head[u]; i != -1; i = edge[i].nex)//遍历当前节点的儿子
{
int v = edge[i].v;
if(vis[v]) continue;//若访问过, 跳过,主要是为了防止查找其父亲节点,若是单向边可以省去
tarjan(v);
Union(v, u);//将v并到u的祖先里
}
vector<Node>::iterator it = q[u].begin();
for(; it != q[u].end(); it++)//需要查找的边里有没有以u为起点
{
if(vis[(*it).v])
{
ans[(*it).id] = find((*it).v);//若访问过,则该组答案就是此时v的祖先
}
}
}
完整代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cstdio>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
const int maxn = 5e5 + 10;
struct node{
int v, nex;
}edge[maxn << 1];
struct Node{
int v, id;
};
int father[maxn], head[maxn], ans[maxn];
int n, m, r, cnt;
vector<Node> q[maxn];
bool vis[maxn << 1];
void init()
{
mem(head, -1);
for(int i = 0; i < maxn; i++)
father[i] = i;
}
void addedge(int u, int v)//链式向前星存边
{
edge[cnt].v = v;
edge[cnt].nex = head[u];
head[u] = cnt++;
}
int find(int x)
{
int cur = x;
while(x != father[x])
{
x = father[x];
}
while(cur != x)
{
int z = cur;
cur = father[z];
father[z] = x;
}
return x;
}
void Union(int x, int fa)
{
int fx = find(x);
int fy = find(fa);
if(fx != fy)
father[fx] = fy;
}
void tarjan(int u)
{
vis[u] = true;//标记
for(int i = head[u]; i != -1; i = edge[i].nex)//遍历当前节点的儿子
{
int v = edge[i].v;
if(vis[v]) continue;//若访问过, 跳过,主要是为了防止查找其父亲节点,若是单向边可以省去
tarjan(v);
Union(v, u);//将v的并到u的祖先里
}
vector<Node>::iterator it = q[u].begin();
for(; it != q[u].end(); it++)//需要查找的边里有没有以u为起点
{
if(vis[(*it).v])
{
ans[(*it).id] = find((*it).v);//若访问过,则该组答案就是此时v的祖先
}
}
}
int main()
{
init();
// cin >> n >> m >> r;
scanf("%d%d%d", &n, &m, &r);
int x, y;
for(int i = 0; i < n - 1; i++)
{
// cin >> x >> y;
scanf("%d%d", &x, &y);
addedge(x, y);
addedge(y, x);
}
for(int i = 0; i < m; i++)
{
// cin >> x >> y;
scanf("%d%d", &x, &y);
Node cur;
cur.v = y;
cur.id = i;
q[x].push_back(cur);
cur.v = x;
q[y].push_back(cur);
}
tarjan(r);
for(int i = 0; i < m; i++)
printf("%d\n", ans[i]);
// cout << ans[i] << endl;
return 0;
}