LCA(Tarjin离线、倍增在线)模版——图论

  • 概述
    LCA(Least Common Ancestors),即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v最近的公共祖先(另一种说法,离树根最远的公共祖先)。

LCA 在线倍增

  • 铺垫

倍增思想
倍增st表



  • 复杂度分析

在线倍增LCA代码

#include<iostream>
using namespace std;

const int mxn = 5e5 + 10;
struct Edge
{
    int v, next;
} edge[mxn * 2];            //注意无向图 x2
int head[mxn], num = 0;
void Add(int u, int v)
{
    edge[++ num] = (Edge){ v, head[u] };
    head[u] = num;
}

int f[mxn][30], dep[mxn];
//dfs 1.找到每个节点的父节点、每个节点的所有祖先 3. 统计每个节点的深度
void dfs(int u, int pre)
{
    f[u][0] = pre;
    dep[u] = dep[pre] + 1;

    for(int i = 1; (1 << i) <= dep[u]; i ++)
        f[u][i] = f[f[u][i - 1]][i - 1];

    for(int i = head[u]; i; i = edge[i].next)
    {
        int v = edge[i].v;
        if(v != pre)
            dfs(v, u);
    }
}
//lca 1. 使两个节点a、b回溯到相同的深度 2. 之后两个节点从相同的深度步步逼近 LCA
int lca(int a, int b)
{
    if(dep[a] < dep[b]) swap(a, b);
    for(int i = 20; i >= 0; i --)
        if(dep[f[a][i]] >= dep[b])
            a = f[a][i];
    if(a == b) return a;

    for(int i = 20; i >= 0; i --)
    {
        if(f[a][i] != f[b][i])
        {
            a = f[a][i];
            b = f[b][i];
        }
    }
    return f[a][0];
}

int main()
{
    int n, m, s;
    scanf("%d %d %d", &n, &m, &s);
    int u, v;
    for(int i = 1; i < n; i ++)
    {
        scanf("%d %d", &u, &v);
        Add(u, v);
        Add(v, u);
    }
    dfs(s, 0);
    while(m --)
    {
        scanf("%d %d", &u, &v);
        printf("%d\n", lca(u, v));
    }

    return 0;
}

例题


LCA离线Tarjan

  • 铺垫
    Tarjin知识铺垫(待加入)


  • 复杂度分析

LCA离线Trajin代码(很大几率存在错误,待修改)

#include<iostream>
#include<cstring>
using namespace std;

const int mxn = 1e5;
struct Edge
{
    int u, v, w, next, lca;
} e1[mxn], e2[mxn];
int h1[mxn], h2[mxn];
int t1, t2;
int vis[mxn], dis[mxn];

void Add(int u, int v, int w, int & t, Edge e[], int h[])
{
    e[++ t] = (Edge){ u, v, w, h[u] }; h[u] = t;
}

int f[mxn];
int find(int x) { return f[x] == x ? x : f[x] = find(f[x]); }
void join(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        f[fx] = fy;
}

void Tarjan_Lca(int u, int w)
{
    f[u] = u;
    dis[u] = w;
    vis[u] = 1;
    for(int i = h1[u]; i; i = e1[i].next)
    {
        Tarjan_Lca(u, w + e1[i].w);
        f[e1[i].v] = u;
    }

    for(int i = h2[u]; i; i = e2[i].next)
    {
        int v = e2[i].v;
        if(vis[v])
        {
            e2[i].lca = find(v);
            e2[i].w = dis[u] + dis[v] - dis[e2[i].lca] * 2;
        }
    }
}

void init(int n)
{
    t1 = t2 = 0;                                //初始化 边 的数量
    for(int i = 1; i <= n; i ++) f[i] = i, vis[i] = 0;      
}

int main()
{

    int n, m, s;
    scanf("%d %d %d", &n, &m, &s);
    init(n);
    int u, v, w;
    for(int i = 1; i < n; i ++)
    {
        scanf("%d %d %d", &u, &v, &w);
        Add(u, v, w, t1, e1, h1);
    }
    for(int i = 1; i <= m; i ++)
    {
        scanf("%d %d", &u, &v);
        Add(u, v, -1, t2, e2, h2);
    }

    Tarjan_Lca(s, 0);
    for(int i = 1; i <= m; i ++)
        printf("%d %d\n", e2[i].lca, e2[i].w);

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/107552621