Tarjan全家桶之最近公共祖先学习记录

6 篇文章 0 订阅
3 篇文章 0 订阅

序言

废话怪现身!
今天我来填坑了!Tarjan全家桶最后一节!
其实你们知道?Tarjan全家桶还有一个求LCA的算法?
对的没有错,这是平时根本不会用到的LCA算法!你们一定都只会倍增,怎么会这种高级东西呢!
这个算法虽然实用性没有倍增算法高,但也不乏一些业界毒瘤考查Tarjan求LCA的。
众人:你废话好多啊!
前排膜拜Tarjan巨佬。
至于学习这方面嘛,学习是不可能学习的,这辈子都不可能学习的,只有靠腐败才能维持得了生活这样子。

前置技能

并查集

DFS搜索

我不信这两个东西有人不会。
前置技能Get!

例题

P3379 【模板】最近公共祖先(LCA)

Tarjan

基本思想

Tarjan算法是一个离线算法,通过DFS遍历原树,同时处理与当前节点相关的询问。

  1. 处理当前节点 u u 的儿子节点si,将 si s i 并入 u u
  2. 处理与点u有关的询问 (u,v) ( u , v ) ,若点 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值