P3379 【模板】最近公共祖先(LCA)【离线算法Tarjan】

感觉离线求LCA就只是用了一个并查集而已,不知道为什么叫Tarjan了(明明Tarjan是用来求有向图Strong Connectivity的说)

来说说怎么用离线算法Tarjan来求LCA啦~

首先我们将读入的查询和查询的序号存起来,因为是离线算法的嘛~而且要存双向的!(为什么看了下边就懂了的)

我们从根开始dfs遍历整棵树,如果搜到叶子结点u了,那么就看下u是不是需要被查询的结点,如果是就看和u有关系的v结点是不是已经被遍历到了,如果是那么LCA(u, v)就是root[ v ]。root[v]是所有u和v所在子树的根结点,因为v已经合并到了目前深度最浅的子树的根结点上了!

所以我们需要有的操作就是在回溯的时候,将儿子结点合并到它的父亲结点上去。

真的模拟一边就懂了。(其实是我解释不太好啦,有种只可意会不可言传的feel...)这个图画的很好,模拟一遍就会懂的。

再附上在线算法求LCA!


#include <iostream>
#include <cstdio>
#include <vector>

using namespace std;

inline int read()
{
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
    while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
    return x * f;
}

const int maxN = 500005;

int n, m, s;
vector<pair<int, int> >q[maxN];
bool vis[maxN];
int lca[maxN];

struct EDGE{
    int adj, to;
    EDGE(int a = -1, int b = 0): adj(a), to(b){}
}edge[maxN << 1];
int head[maxN], cnt;

void add_edge(int u, int v)
{
    edge[cnt] = EDGE(head[u], v);
    head[u] = cnt ++ ;
}

int root[maxN];
void init()
{
    for(int i = 0; i <= n; ++ i )
    {
        head[i] = -1;
        root[i] = i;
        q[i].clear();
        vis[i] = false;
    }
    cnt = 0;
}
int Find(int x) {return root[x] == x ? x : root[x] = Find(root[x]); }
bool Same(int x, int y) { return Find(x) == Find(y); }
void Merge(int x, int y) //将y合并到x上
{
    x = Find(x); y = Find(y);
    root[y] = x;
}

void Tarjan(int u, int fa)
{
    vis[u] = true;
    for(int i = head[u]; ~i; i = edge[i].adj)
    {
        int v = edge[i].to;
        if(v != fa)
        {
            Tarjan(v, u);
            if(!Same(u, v)) Merge(u, v);
        }
    }
    if(!q[u].empty())
    {
        int siz = q[u].size();
        for(int i = 0; i < siz; ++ i )
        {
            if(vis[q[u][i].first])
                lca[q[u][i].second] = Find(q[u][i].first);
        }
    }
}

int main()
{
    n = read(); m = read(); s = read();
    init();
    for(int i = 0; i < n - 1; ++ i )
    {
        int u, v; u = read(); v = read();
        add_edge(u, v);
        add_edge(v, u);
    }
    for(int i = 0; i < m; ++ i )
    {
        int u, v; u = read(); v = read();
        q[u].push_back(make_pair(v, i));
        q[v].push_back(make_pair(u, i));
    }
    Tarjan(s, 0);
    for(int i = 0; i < m; ++ i )
        printf("%d\n", lca[i]);
    return 0;
}
发布了242 篇原创文章 · 获赞 68 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44049850/article/details/104338805