最近公共祖先 LCA——树上倍增

给定一棵包含 n 个节点的有根无向树,节点编号互不相同,但不一定是 1∼n 。

有 m 个询问,每个询问给出了一对节点的编号 x 和 y ,询问 x 与 y 的祖孙关系。

输入格式
输入第一行包括一个整数 表示节点个数;

接下来 n 行每行一对整数 a 和 b ,表示 a 和 b 之间有一条无向边。如果 b 是 −1 ,那么 a 就是树的根;

第 n+2 行是一个整数 m 表示询问个数;

接下来 m 行,每行两个不同的正整数 x 和 y ,表示一个询问。

输出格式
对于每一个询问,若 x 是 y 的祖先则输出 1 ,若 y 是 x 的祖先则输出 2 ,否则输出 0 。

数据范围
1≤n,m≤4×104 ,
1≤每个节点的编号≤4×104
输入样例:
10
234 -1
12 234
13 234
14 234
15 234
16 234
17 234
18 234
19 234
233 19
5
234 233
233 12
233 13
233 15
233 19
输出样例:
1
0
0
0
2

#include <bits/stdc++.h>

using namespace std;
int cnt;
const int N = 100005;
struct node 
{
    int to, ne;
} a[N];
int start;
int head[N];
int pre[N][25];
void add(int x, int y) 
{
    cnt++;
    a[cnt].to = y;
    a[cnt].ne = head[x];
    head[x] = cnt;
}
int n, m;
int deep[N];
void dfs(int x) 
{
    for (int i = head[x]; i != -1; i = a[i].ne)
     {
        if (!deep[a[i].to]) 
        {
            deep[a[i].to] = deep[x] + 1;
            //cout <<deep[a[i].to]<<endl;
            pre[a[i].to][0] = x;
            dfs(a[i].to);
        }
    }
}
int lca(int x, int y) 
{
    int ans = 1;
    if (deep[x] > deep[y])
     {
        swap(x, y);
        ans = 2;
    } 
    for (int i = 20; i >= 0; i--) 
    {
        if (deep[pre[y][i]] >= deep[x]) 
        y = pre[y][i];
    }
    return x==y?ans:0;
}
int main() 
{
    memset(head, -1, sizeof(head));
    cin >> n;
    for (int i = 1; i <= n; i++)
     {
        int x, y;
        cin >> x >> y;

        if (y == -1)
         {
            start = x;
        } 
        else 
        {
            add(x, y);
            add(y, x);
        }
    }
    deep[start] = 1;
    //cout <<start<<endl;
    dfs(start);
    for (int i = 1; i <= 20; i++)
     {
        for (int j = 1; j <= 40000; j++) 
        pre[j][i] = pre[pre[j][i - 1]][i - 1];
    }
    cin >> m;
    for (int i = 1; i <= m; i++) 
    {
        int x, y;
        cin >> x >> y;
        cout << lca(x, y) << endl;
    }
    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值