当前位置: 首页 > news >正文

蓝桥杯 2023 省赛 B 组 I 题 - 景区导游题解(LCA最近公共祖先)

蓝桥杯 2023 省赛 B 组 I 题 - 景区导游题解

题目分析

这道题目描述了一个树状结构的景区,景点之间通过摆渡车线路相连。我们需要计算在跳过游览路线中某一个景点后,整个游览路线所需的总时间。

解题思路

  1. 树结构处理:景点之间形成一棵树,我们需要高效地计算树上任意两点之间的距离。
  2. LCA(最近公共祖先):为了快速计算树上两点间距离,我们可以使用LCA技术。两点u和v之间的距离等于dist[u] + dist[v] - 2 * dist[lca(u,v)],其中dist[u]表示从根节点到u的距离。
  3. 预处理:使用广度优先搜索(BFS)预处理每个节点的深度、到根节点的距离以及倍增数组,用于快速查询LCA。
  4. 路线计算:对于原定游览路线,先计算完整路线总时间。然后对于每个可能跳过的景点,计算跳过后的路线时间:
    • 跳过第一个景点:总时间减去第一个景点到第二个景点的时间
    • 跳过最后一个景点:总时间减去倒数第二个景点到最后一个景点的时间
    • 跳过中间某个景点:总时间减去前一个景点到当前景点的时间,减去当前景点到后一个景点的时间,再加上前一个景点直接到后一个景点的时间

代码实现

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10, M = N * 2;
int n, m;
int arr[N];
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

int f[N][16], depth[N];
int dist[N];
int q[M];

// 预处理每个节点的深度和倍增数组
void bfs()
{
    depth[1] = 1; // 1是第一层源点
    int tt = -1, hh = 0;
    q[++tt] = 1;
    while (hh <= tt)
    {
        auto u = q[hh++];
        for (int i = h[u]; i != -1; i = ne[i])
        {
            int v = e[i];
            if (!depth[v])
            { // 该点未被处理
                q[++tt] = v;
                depth[v] = depth[u] + 1; // 深度 + 1
                dist[v] = dist[u] + w[i];
                f[v][0] = u;                  // u是当前节点的父节点
                for (int k = 1; k <= 15; k++) // 当前节点倍增往上跳
                    f[v][k] = f[f[v][k - 1]][k - 1];
            }
        }
    }
}

int lca(int a, int b)
{
    // 将两个节点跳到同深度
    if (depth[a] < depth[b])
        swap(a, b); // 确保a在b的下方
    for (int k = 15; k >= 0; k--)
        if (depth[f[a][k]] >= depth[b])
            a = f[a][k];

    // 两个节点同时向上跳
    if (a == b)
        return a;
    for (int k = 15; k >= 0; k--)
        if (f[a][k] != f[b][k])
            a = f[a][k], b = f[b][k];
    // 返回两个节点的父节点
    return f[a][0];
}

int getDis(int a, int b)
{
    return dist[a] + dist[b] - 2 * dist[lca(a, b)];
}

void solve()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for (int i = 1; i < n; i++)
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }

    bfs();
    long long tot = 0; // 总时间
    for (int i = 1; i <= m; i++)
        cin >> arr[i];

    // 计算完整路线总时间
    for (int i = 1; i < m; i++)
        tot += getDis(arr[i], arr[i + 1]);

    // 计算跳过每个景点后的时间
    for (int i = 1; i <= m; i++)
    {
        int ans = 0;
        if (i == 1)
        { // 跳过第一个景点
            ans = tot - getDis(arr[i], arr[i + 1]);
        }
        else if (i == m)
        { // 跳过最后一个景点
            ans = tot - getDis(arr[i - 1], arr[i]);
        }
        else
        { // 跳过中间景点
            ans = tot - getDis(arr[i - 1], arr[i]) - getDis(arr[i], arr[i + 1]) + getDis(arr[i - 1], arr[i + 1]);
        }
        cout << ans << " ";
    }
}

signed main()
{
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    solve();
    return 0;
}

复杂度分析

  1. 预处理阶段:BFS遍历整棵树,时间复杂度O(N)。每个节点处理16次倍增数组,总时间复杂度O(N log N)。
  2. 查询阶段
    • 计算完整路线时间:O(K)
    • 每个查询处理:O(1)(因为已经预处理了LCA和距离)
    • 总查询时间复杂度:O(K)

因此,算法的总时间复杂度为O(N log N + K),能够高效处理题目给出的数据规模。

总结

本题的关键在于利用LCA技术高效计算树上两点间距离,并通过预处理将查询时间优化到常数级别。这种方法适用于需要频繁查询树上路径问题的场景。

相关文章:

  • 水下成像机理分析
  • 输电线路在线监测通信规约,即I1协议
  • 医院信息系统建设:大数据平台、集成平台、数据治理平台,该如何选择?
  • 常用shell命令
  • NodeTextFileCollectorScrapeError 报警原因及解决方法
  • Linux中的权限管理(附加详细实验示例)
  • swe-bench环境安装踩坑
  • 如何利用 AI 进行 A/B 测试的优化?
  • 【设计模式】过滤器模式
  • 手机显示5GA图标的条件
  • Oracle中文一二三四排序【失败】
  • Linux防火墙的iptables命令示例与详细解释
  • deepseek v3-0324实现数学方程式绘制曲线功能
  • Redis-14.在Java中操作Redis-Spring Data Redis使用方式-操作列表类型的数据
  • 西门子TCP通讯过程中硬件连接突然断开
  • 轻帆云智能ITSM应用最佳实践,助力IT共享服务中心高效运营
  • Redis:集群
  • 广告推荐算法:COSMO算法与A9算法的对比
  • 如何让AI帮你做用户运营:用户消费偏好分层和洞察
  • Java之JDBC数据库连接技术
  • 来论|受美国“保护”,日本民众要付出什么代价?
  • 光明日报:回应辅警“转正”呼声,是一门政民互动公开课
  • 中办、国办印发《安全生产考核巡查办法》
  • 这座“蚌埠住了”的城市不仅会接流量,也在努力成为文旅实力派
  • 上海74岁老人宜春旅游时救起落水儿童,“小孩在挣扎容不得多想”
  • 卡尼领导的加拿大自由党在联邦众议院选举中获胜