📜  门|门 IT 2008 |第 52 题(1)

📅  最后修改于: 2023-12-03 14:58:36.459000             🧑  作者: Mango

题目介绍

这是门|门 IT 2008比赛中的第52题,考察了程序员对于算法和数据结构的理解和使用。

题目描述

给定一棵有根树以及树中的一些点。对于每个点,求出其到根节点路径上(包括自身)各个点之间的距离。

输入格式

第1行2个整数,N和M,表示树的节点数量和指定的点的数量。

接下来N-1行,每行2个整数u和v,表示一条边(u,v)。

接下来M行,每行一个整数p,表示指定的点的编号。

输出格式

共M行,每行N个数,其中第i个数表示第i个指定点到根节点路径上第j个点(包括自身)的距离,每个数占一行。

代码实现

题目中要求计算树上两点间距离,我们可以考虑使用树的LCA算法来实现,具体实现方式如下:

#include <iostream>
#include <vector>

using namespace std;

const int N = 100010;

int n, m;
int depth[N], fa[N][20];  //每个节点深度和所在的2^k个祖先节点
vector<int> g[N];

void dfs(int u, int father)   //预处理深度和2^k祖先节点
{
    depth[u] = depth[father] + 1;
    fa[u][0] = father;
    for (int i = 1; i <= 19; i++)
        fa[u][i] = fa[fa[u][i - 1]][i - 1];

    for (int v : g[u])
        if (v != father)
            dfs(v, u);
}

int lca(int a, int b)   //计算两点距离
{
    if (depth[a] < depth[b])
        swap(a, b);

    for (int i = 19; i >= 0; i--)
        if (depth[fa[a][i]] >= depth[b])
            a = fa[a][i];

    if (a == b)
        return a;

    for (int i = 19; i >= 0; i--)
        if (fa[a][i] != fa[b][i])
            a = fa[a][i], b = fa[b][i];

    return fa[a][0];
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < n - 1; i++)
    {
        int a, b;
        cin >> a >> b;
        g[a].push_back(b);
        g[b].push_back(a);
    }

    dfs(1, 0);

    while (m--)
    {
        int p;
        cin >> p;

        for (int i = 1; i <= n; i++)
            cout << depth[i] - depth[lca(i, p)] << endl;
    }

    return 0;
}
算法分析

本题使用树的LCA算法进行求解,时间复杂度为$O(n\log_2n)$,其中$n$为树的节点数量。

参考文献

[1] 《算法竞赛进阶指南》第三章 树与图的深入应用

[2] ACWING官方题解