序言
废话怪现身!
今天我来填坑了!Tarjan全家桶最后一节!
其实你们知道?Tarjan全家桶还有一个求LCA的算法?
对的没有错,这是平时根本不会用到的LCA算法!你们一定都只会倍增,怎么会这种高级东西呢!
这个算法虽然实用性没有倍增算法高,但也不乏一些业界毒瘤考查Tarjan求LCA的。
众人:你废话好多啊!
前排膜拜Tarjan巨佬。
至于学习这方面嘛,学习是不可能学习的,这辈子都不可能学习的,只有靠腐败才能维持得了生活这样子。
前置技能
并查集
DFS搜索
我不信这两个东西有人不会。
前置技能Get!
例题
Tarjan
基本思想
Tarjan算法是一个离线算法,通过DFS遍历原树,同时处理与当前节点相关的询问。
- 处理当前节点 u u 的儿子节点,将 si s i 并入 u u
- 处理与点有关的询问 (u,v) ( u , v ) ,若点 v v 被处理过,则点的并查集顶点即为 (u,v) ( u , v ) 的LCA,否则跳过。
Code
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
struct edge
{
int v, next, num;
};
int n, m, s;
bool bj[500005];
int f[500005];
int l[500005], r[500005], lca[500005];
int final[500005], sf[500005], tot;
edge son[1000005], d[1000005];
void Dfs(int x)
{
bj[x] = true;
for (int i = final[x]; i; i = d[i].next)
if (!bj[d[i].v])
{
tot++;
son[tot].v = d[i].v;
son[tot].next = sf[x];
sf[x] = tot;
Dfs(d[i].v);
}
}
int Find(int x)
{
if (!f[x])
return x;
return f[x] = Find(f[x]);
}
void Tarjan(int u)
{
for (int i = sf[u]; i; i = son[i].next)
{
int v = son[i].v;
Tarjan(v);
f[v] = u;
bj[v] = true;
}
for (int i = final[u]; i; i = d[i].next)
if (bj[d[i].v])
lca[d[i].num] = Find(d[i].v);
}
int main(int argc, char const *argv[])
{
//freopen("init.in", "r", stdin);
scanf("%d%d%d", &n, &m, &s);
for (int i = 1; i <= n - 1; i++)
{
int a, b;
scanf("%d%d", &a, &b);
tot++;
d[tot].v = b;
d[tot].next = final[a];
final[a] = tot;
tot++;
d[tot].v = a;
d[tot].next = final[b];
final[b] = tot;
}
tot = 0;
Dfs(s);
memset(final, 0, sizeof(final));
tot = 0;
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &l[i], &r[i]);
tot++;
d[tot].v = r[i];
d[tot].num = i;
d[tot].next = final[l[i]];
final[l[i]] = tot;
tot++;
d[tot].v = l[i];
d[tot].num = i;
d[tot].next = final[r[i]];
final[r[i]] = tot;
}
Tarjan(s);
for (int i = 1; i <= m; i++)
printf("%d\n", lca[i]);
return 0;
}