Tarjan入门⑤离线求LCA

前言:

md,感觉LCA上我的学习顺序正好反过来了。先学的ST+欧拉序,后学的倍增。最后才学的Tarjan求LCA。

这个算法唯一不足就是离线的,但是一大好处就是实现起来超级简单。理解起来也很简单.

本质上就是一个dfs。

我们对每一个点开一个vector存询问。对于任意两个点 u , v u,v u,v。dfs的时候要么先遍历u再遍历v.要么反之。所以我们需要对 u , v u,v u,v都插入询问。

当访问到 v v v时,若 u u u已经被访问了,那么 L C A ( u , v ) LCA(u,v) LCA(u,v)这个点一定是被访问且未结束访问的点。通过 u u u L C A ( u , v ) LCA(u,v) LCA(u,v)可以用并查集来优化。

讲的太乱了,还是看这篇博客吧:
https://segmentfault.com/a/1190000015145319?utm_source=index-hottest

具体看代码:

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int maxn = 5e5 + 5;
const int maxm = 1e6 + 5;
int u[maxm] , v[maxm] , nextt[maxm] ,first[maxn] , sign;
void addedge(int x , int y){u[++sign]=x;v[sign]=y;nextt[sign]=first[x];first[x]=sign;}
vector<pii> q[maxn];
int res[maxm];
int f[maxn];
int getf (int x) {return x == f[x] ? x : f[x] = getf(f[x]);}
bool mer (int a , int b)
{
    int fa = getf(a) , fb = getf(b);
    if (fa == fb) return false;
    f[fb] = fa;
    return true;
}
int vis[maxn];
void dfs (int g , int fa)
{
    vis[g] = 1;
    for (int i = first[g] ; i ; i = nextt[i]){
        if (v[i] == fa) continue;
        dfs(v[i] , g);
        mer(g , v[i]);
    }
    for (auto & k : q[g]){
        if (vis[k.first] == 0) continue;
        res[k.second] = getf(k.first);
       // cout << "现在在" << g << " " << k.first << " " << getf(k.first) << endl;
    }
}
int main()
{
    int n , m , s;
    scanf("%d%d%d" , &n , &m , &s);
    for (int i = 1 ; i <= n ; i++){
        f[i] = i;
    }
    for (int i = 1 ; i < n ; i++){
        int x , y;
        scanf("%d%d" , &x , &y);
        addedge(x , y);
        addedge(y , x);
    }
    for (int i = 1 ; i <= m ; i++){
        int x , y;
        scanf("%d%d" , &x , &y);
        q[x].push_back(make_pair(y , i));
        q[y].push_back(make_pair(x , i));
    }
    dfs(s , 0);
    for (int i = 1 ; i <= m ; i++){
        printf("%d\n" , res[i]);
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值