「学习笔记」tarjan求最近公共祖先
Posted 朝气蓬勃 后生可畏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「学习笔记」tarjan求最近公共祖先相关的知识,希望对你有一定的参考价值。
Tarjan 算法是一种 离线算法,需要使用并查集记录某个结点的祖先结点。
并没有传说中的那么快。
过程
将询问都记录下来,将它们建成正向边和反向边。
在 dfs 的过程中,给走过的节点打上标记,同时维护并查集,这里利用了回溯的思想,如果 \\(u\\) 节点的这棵子树没搜完,那么 fa[u] = u;
,搜完后,在更新并查集。
我们假设查询 \\(u\\) 和 \\(v\\) 的最近公共祖先,搜到节点 \\(u\\),如果另一个节点 \\(v\\) 已经被搜到过了,那么 \\(v\\) 点的并查集祖先就是 \\(u\\) 和 \\(v\\) 的最近公共祖先。
如果第一次查询 \\(v\\) 点时,发现 \\(v\\) 点已经被搜到了,说明 \\(u\\) 和 \\(v\\) 点在同一棵子树中,并且这个子树是所有包括了 \\(u\\) 点和 \\(v\\) 点的子树中
siz
最小的,这棵子树的根也是在所有符合条件的子树的根中离 \\(u\\) 和 \\(v\\) 最近的,即这棵子树的根就是 \\(u\\) 和 \\(v\\) 的最近公共祖先,而 \\(v\\) 的并查集祖先就是这个根节点。
代码
结构体记录询问
int cnt = 1;
struct query
int v, lca, nxt;
q[N << 1];
记录询问、初始化
for (int i = 1, x, y; i <= m; ++ i)
scanf("%d%d", &x, &y);
add_query(x, y);
add_query(y, x);
for (int i = 1; i <= n; ++ i)
fa[i] = i;
tarjan、并查集
void tarjan(int u)
fa[u] = u;
vis[u] = 1;
for (int v : son[u])
if (!vis[v])
tarjan(v);
fa[v] = u;
int v;
for (int i = h[u]; i; i = q[i].nxt)
if (vis[v = q[i].v])
q[i].lca = q[i ^ 1].lca = find(v);
fa[u] = u;
是为了保证在 \\(u\\) 这棵子树没有搜完的情况下,让它子树里的节点的并查集找祖先时找到的是它。
Tarjan 算法求 LCA / Tarjan 算法求强连通分量
- 【时光蒸汽喵带你做专题】最近公共祖先 LCA (Lowest Common Ancestors)_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
- tarjan LCA - YouTube
- Tarjan算法_LCA - A_Bo的博客 - CSDN博客
- Tarjan离线算法求最近公共祖先(LCA) - 初学者 - CSDN博客
- 最近公共祖先(LCA) - riteme.site
- Fuzhou University OnlineJudge
- P3379 【模板】最近公共祖先(LCA) - 洛谷 | 计算机科学教育新生态
- 有向图强连通分量的Tarjan算法 - BYVoid
- Tarjan算法 详解+心得 - Christopher_Yan - 博客园
- UESTCACM 每周算法讲堂第25期 Tarjan求强连通分量_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili
洛谷P3379 【模板】最近公共祖先(LCA)
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <set>
#include <stack>
#include <map>
#include<vector>
using namespace std;
const int maxn = 5e5 + 5;
struct query
{
int x;
int y;
int lca;
}query[maxn];
int fa[maxn];
bool vis[maxn];
int deep[maxn];
vector<int> G[maxn];
vector<int> Q[maxn];
void init()
{
for (int i = 0; i < maxn; i++)
{
fa[i] = i;
}
}
int find(int x)
{
if (fa[x] == x)
return x;
else
return fa[x] = find(fa[x]);
}
void Union(int v, int u)
{
int vfa = find(v), ufa = find(u);
if (vfa == ufa)return;
else fa[v] = u;
}
void tarjan(int u)
{
vis[u] = true;
for (auto qid : Q[u]) {
if (query[qid].x == u) {
if (vis[query[qid].y]) {
query[qid].lca = find(query[qid].y);
}
}
else
{
if (vis[query[qid].x]) {
query[qid].lca = find(query[qid].x);
}
}
}
for (auto v : G[u]) {
if (vis[v])
continue;
deep[v] = deep[u] + 1;
tarjan(v);
Union(v, u);
}
}
int main()
{
init();
int n, m, s;
scanf_s("%d%d%d", &n, &m, &s);
int x, y;
for (int i = 1; i < n; i++)
{
scanf_s("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
for (int i = 1; i <= m; i++)
{
scanf_s("%d%d", &query[i].x, &query[i].y);
Q[query[i].x].push_back(i); // 对于节点 x 来说有一个编号为 i 的询问
Q[query[i].y].push_back(i);
}
tarjan(s);
for (int i = 1; i <= m; i++)
{
printf("%d
", query[i].lca);
}
for (int i = 1; i <= n; i++)
{
printf("节点 %d 的深度是 %d
", i, deep[i]);
}
}
FOJ 1628 计算公共祖先的个数
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include <set>
#include <stack>
#include <map>
#include<vector>
using namespace std;
const int maxn = 5e5 + 5;
struct query
{
int x;
int y;
int lca;
}query[maxn];
int fa[maxn];
bool vis[maxn];
int deep[maxn];
vector<int> G[maxn];
vector<int> Q[maxn];
void init()
{
for (int i = 0; i < maxn; i++)
{
fa[i] = i;
}
}
int find(int x)
{
if (fa[x] == x)
return x;
else
return fa[x] = find(fa[x]);
}
void Union(int v, int u)
{
int vfa = find(v), ufa = find(u);
if (vfa == ufa)return;
else fa[v] = u;
}
void tarjan(int u)
{
vis[u] = true;
for (auto qid : Q[u]) {
if (query[qid].x == u) {
if (vis[query[qid].y]) {
query[qid].lca = find(query[qid].y);
}
}
else
{
if (vis[query[qid].x]) {
query[qid].lca = find(query[qid].x);
}
}
}
for (auto v : G[u]) {
if (vis[v])
continue;
deep[v] = deep[u] + 1;
tarjan(v);
Union(v, u);
}
}
int main()
{
init();
int n, k, m;
int x, y;
scanf_s("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf_s("%d", &k);
for (int j = 1; j <= k; j++)
{
scanf_s("%d", &y);
G[i].push_back(y);
G[y].push_back(i);
}
}
scanf_s("%d", &m);
for (int i = 1; i <= m; i++) // 离线处理
{
scanf_s("%d%d", &query[i].x, &query[i].y);
Q[query[i].x].push_back(i); // 对于节点 x 来说有一个编号为 i 的询问
Q[query[i].y].push_back(i);
}
tarjan(1);
/*for (int i = 1; i <= m; i++)
{
printf("%d
", query[i].lca);
}
for (int i = 1; i <= n; i++)
{
printf("节点 %d 的深度是 %d
", i, deep[i]);
}*/
for (int i = 1; i <= m; i++)
{
printf("%d
", deep[query[i].lca] + 1);
}
}
以上是关于「学习笔记」tarjan求最近公共祖先的主要内容,如果未能解决你的问题,请参考以下文章