[图论]Count Common Ancestors

本文介绍了一种使用Tarjan算法解决树结构中两节点共同祖先数量的问题,通过高效的O(N+Q)算法处理节点数N和查询数Q,详细解释了算法流程,包括节点遍历、深度计算、并查集合并等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Count Common Ancestors


Time Limit:6sMemory limit:32M
Accepted Submit:74Total Submit:349

This problem is very simple and well known.

It is shown as follows:

There is a tree which has n nodes, and the nodes are numbered from 1 to n, No. 1 is always the root of the tree. Given you two nodes of the tree, you should count the total number of their common ancestors.

Input

 

There are mulitiple tests. For each test, the first line is an integer n(1<=n<=50000), following n lines, the i+1-th line denotes the information of the i-th node. For each line, there is an integer k(0<=k<=n-1), denoting the number of direct children of the i-th node. Then following k integers, denoting the No. of the i-th node. When k is equal to 0, denoting the i-th node is leaf node. You should note that the node can be the ancestor of itself.

The n+2-th line is an integer m (1<=m<=30,000), denoting there are m queries. Following m lines, for each line, there are two integers x and y, denoting the No. of the two nodes.

Output

 

For each query, you should count the total number of their common ancestors.

Sample Input

12
3 2 3 4
2 5 6
0
0
2 7 8
2 9 10
0
0
0
2 11 12
0
0
6
3 11
7 12
4 8
9 12
8 10
1 12

Sample Output

1
2
1
3
2
1
/*
  Name: tarjan 算法求 LCA 
  Author: Micklongen
  Description: O(N + Q),N表示节点数,Q表示查询数 
*/

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>

using namespace std;

struct Node
{
    int visit;
    int child;
    int depth;
    int brother;
};
struct Node node[50010];

struct List_node
{
    int index;
    int node;
    int next;
};

int header[50010];
struct List_node list_node[100010];
int top;

int out[50010];

int father[50010];

int find(int u)
{
    return father[u] == u ? u : (father[u] = find(father[u]));
}

int merge(int u, int v)
{
    int a = find(u);
    int b = find(v);

    if (a != b)
        father[b] = a;

    return 0;
}

int com_depth(int root, int depth)
{
    int child, list;

    node[root].depth = depth;

    child = node[root].child;
    father[root] = root;

    while(child != -1)
    {
        com_depth(child, depth + 1);
        merge(root, child);
        child = node[child].brother;
    }

    node[root].visit = 1;
    list = header[root];
    while (list != -1)
    {
        if (node[list_node[list].node].visit == 1)
            out[list_node[list].index] = node[find(list_node[list].node)].depth;
        list = list_node[list].next;
    }

    return 0;
}

int main()
{
    int i, j;
    int v, num, inum;
    int a, b;

    while (scanf("%d", &num) != EOF)
    {
        for (i = 1; i <= num; ++i)
        {
            header[i] = -1;
            node[i].child = -1;
            node[i].visit = 0;

            scanf("%d", &inum);
            for (j = 1; j <= inum; ++j)
            {
                scanf("%d", &v);
                node[v].brother = node[i].child;
                node[i].child = v;
            }
        }

        top = 0;
        scanf("%d", &num);
        for (i = 1; i <= num; ++i)
        {
            scanf("%d %d", &a, &b);

            list_node[top].index = i;
            list_node[top].node = b;
            list_node[top].next = header[a];
            header[a] = top;
            ++top;

            list_node[top].index = i;
            list_node[top].node = a;
            list_node[top].next = header[b];
            header[b] = top;
            ++top;
        }

        com_depth(1, 1);

        for (i = 1; i <= num; ++i)
            printf("%d\n", out[i]);
    }

    return 1;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值