LCA之Tarjan离线算法

LCA 之Tarjan

最近回顾之前学的算法,今天之后冲刺网络流,后缀函数,后缀自动机,回文自动机,二分图,树链剖分,树论进阶,以及数学/数论这块。

先说说LCA,LCA最近公共祖先,有三种算法,一种是朴素算法,就是最简单的两个点往上找祖宗。第二种是基于st表的优化,第三种就是tarjan了,是一个把问题先处理好再输出的算法(离线算法)。

例题

一、并查集,Tarjan是基于深搜的,并查集存储父节点信息。

这个是查询函数:
int find(int x)
{
if (x == fa[x])
return x;
return fa[x] = find(fa[x]);
}

二、存边,存查询,存边用的链式前向星,存查询也用链式前向星,这里关键点在于,第i个查询的比如是x,y的公共祖先,那么把y,i这两个条件存在x的链表下,把x,i存在y的链表下

代码实现如下:
struct edge
{
int to, next;
} e[N << 1];
struct q
{
int to, num, next;
} q[N << 1];
void add(int x, int y)
{
e[++cnt].to = y;
e[cnt].next = h[x];
h[x] = cnt;
}
void qq(int x, int y, int num)
{
q[++cnt2].to = y;
q[cnt2].next = qh[x];
q[cnt2].num = num;
qh[x] = cnt2;
}

第三部分也是最关键的思想:

void tarjan(int u)
{
vis[u] = 1;
for (int i = h[u]; i; i = e[i].next)
if (!vis[e[i].to])
{
tarjan(e[i].to);
fa[e[i].to] = u;
} //1
for (int i = qh[u]; i; i = q[i].next)
if (vis[q[i].to])
{
ans[q[i].num] = find(q[i].to);
}//2
}
第一个for循环是遍历该节点的儿子,遍历完之后再把这个节点的并查集映射设为他的爸爸,之后遍历他的查询,如果他查询的另一个对象已经查询过了,那么就记录到ans数组里,存为答案。

AC代码如下:

#include <bits/stdc++.h>
using namespace std;
#define N 100
int n, m, cnt2 = 0, cnt = 0, root, qh[N], h[N], fa[N], ans[N];
bool vis[N];
int find(int x)
{
if (x == fa[x])
return x;
return fa[x] = find(fa[x]);
}
struct edge
{
int to, next;
} e[N << 1];
struct q
{
int to, num, next;
} q[N << 1];
void add(int x, int y)
{
e[++cnt].to = y;
e[cnt].next = h[x];
h[x] = cnt;
}
void qq(int x, int y, int num)
{
q[++cnt2].to = y;
q[cnt2].next = qh[x];
q[cnt2].num = num;
qh[x] = cnt2;
}
void tarjan(int u)
{
vis[u] = 1;
for (int i = h[u]; i; i = e[i].next)
if (!vis[e[i].to])
{
tarjan(e[i].to);
fa[e[i].to] = u;
}
for (int i = qh[u]; i; i = q[i].next)
if (vis[q[i].to])
{
ans[q[i].num] = find(q[i].to);
}
}
int main()
{
cin >> n >> m >> root;
for (int i = 1; i <= n; i++)
fa[i] = i;
for (int i = 1; i < n; i++)
{
int x, y;
cin >> x >> y;
add(x, y);
add(y, x);
}
for (int i = 1; i <= m; i++)
{
int x, y;
cin >> x >> y;
qq(x, y, i);
qq(y, x, i);
}
tarjan(root);
for (int i = 1; i <= m; i++)
cout << ans[i] << endl;
return 0;
}

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值