LCA最近公共祖先问题(Tarjan离线算法)

参考:点击打开链接

觉得链接处的代码有的地方好像有问题,然后自己改了改拿了过来。。

问题描述:给定一个有根树,n个节点和n-1条边 以及q个查询 查询两个点的LCA  要求按输入顺序输出q个查询的答案。


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <vector>
using namespace std;

const int maxn = 10000 + 7;
struct node {
    int v, id;
};

int n, q, root;
int in[maxn];   /// 入度
int p[maxn], ran[maxn];  ///并查集
int ans[maxn], vis[maxn], res[maxn];
vector<int> G[maxn];    /// 树
vector<node> que[maxn];  /// 查询

int Find(int x) {
    return p[x] == x ? x : p[x] = Find(p[x]);
}

void Union(int b, int a) {
    int x = Find(a), y = Find(b);
    if(x != y) {
        if(ran[x] > ran[y]) p[y] = x;
        else p[x] = y, ran[y] += ran[x] == ran[y];
    }
}

void tarjan(int u) {
    vis[u] = 1;
    for(int i = 0; i < G[u].size(); ++i) {
        tarjan(G[u][i]);
        Union(u, G[u][i]);
        ans[Find(u)] = u;
    }
    for(int i = 0; i < que[u].size(); ++i) {
        if(vis[que[u][i].v]) {
            res[que[u][i].id] = ans[Find(que[u][i].v)];
            //printf("%d %d %d\n", u, que[u][i].v, ans[Find(que[u][i].v)]);
        }
    }
}

void init() {
    memset(in, 0, sizeof(in));
    memset(ran, 0, sizeof(ran));
    for(int i = 0; i <= n; ++i) G[i].clear(), que[i].clear(), p[i] = i;
    root = 0;
}

void input() {
    scanf("%d%d", &n, &q);
    int u, v;
    init();
    for(int i = 1; i < n; ++i) {
        scanf("%d%d", &u, &v);
        ++in[v];
        G[u].push_back(v);
    }
    for(int i = 0; i < q; ++i) {
        scanf("%d%d", &u, &v);
        que[u].push_back((node){v, i});
        que[v].push_back((node){u, i});
    }
    for(int i = 1; i <= n; ++i) if(!in[i]) {
        root = i;
        break;
    }
}

int main() {
    input();
    tarjan(root);
    for(int i = 0; i < q; ++i)
        printf("%d\n", res[i]);
    return 0;
}


/**

7 5
1 3
3 5
5 7
3 4
1 2
2 6
4 7
4 5
6 7
3 2
4 6

**/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值