洛谷 P10289 [GESP样题 八级] 小杨的旅游 C++ 完整题解

一、题目链接

P10289 [GESP样题 八级] 小杨的旅游 - 洛谷

二、题目大意

n个节点之间有n - 1条边,其中k个节点是传送门,任意两个传送门之间可以 以0单位地时间相互到达。问从u到v至少需要多少时间?

三、解题思路

输入不必多讲。

cin >> n >> k >> q;
for (int i = 1; i <= n - 1; i++) {
    int x, y;
    cin >> x >> y;
    e[x].push_back(y);
    e[y].push_back(x);
}
for (int i = 1; i <= k; i++) 
    cin >> door[i];

BFS?DFS?

对于这个题目,你肯定会想到用DFS和BFS直接去做。

或者

当然了,更好的方法一定还有,本文只是介绍了一种方法。

正解

分成两种方案:使用传送门和不使用传送门,取快者。

使用传送门

用BFS找到每个节点离最近传送点的距离存入dst数组,结果就是:从起点走到最近传送点 -> 传送到离终点最近的传送点 -> 走到终点 dst[u] + dst[v]

int dst[N]; // dst[i] = i 到 离i最近的传送点 的距离

void bfs() {
    memset(dst, 0x3f, sizeof (dst));
    queue<int> qu;
    for (int i = 1; i <= k; i++) {
        qu.push(door[i]); // 存入所有的传送点
        dst[door[i]] = 0;
    }
    while (!qu.empty()) {
        int now = qu.front();
        qu.pop();
        for (int x : e[now]) {
            if (dst[x] == 0x3f3f3f3f) {
                dst[x] = dst[now] + 1; // 更新x离最近传送点的位置
                qu.push(x);
            }
        }
    }
}
...
{
...
    bfs();
    int res1 = dst[u] + dst[v];
...
}
...

不使用传送门

普通的BFS方法超时了,这里介绍一种可行的方法(LCA)

什么是LCA

LCA的意思是最近公共祖先,如下图,A与B的最近公共祖先是X。

如何用LCA计算A到B的距离
距离dist

首先计算每个节点离根的距离,也就是深度 - 1。

A与B的距离

= dist[A] + dist[B] - 2 * dist[x]

建立dist

int dist[N] = {-1}; // dist[0] = -1 dist[1]才能 = 0
void dfs(int fa, int x) {
    dist[x] = dist[fa] + 1;
    for (int u : e[x]) {
        if (u != fa) // 不能走回头路 省去vis数组
            dfs(x, u);
    }
}
...
{
...
    dfs(0, 1); // 起初给一个无意义的0作为根节点的父亲
...
}
...

接着是lca函数:

int lca(int u, int v);

lca函数中有两个工作要做

1. 把u和v的深度化作一样 循环上移u或者v,直到dist[u] == dist[v]

// 半伪代码
if (dist[u] > dist[v])
    swap(u, v); // 保证v更深 要不停更新v
while (dist[u] < dist[v]) {
    v = v的父亲;
}
if (u == v) // 如果在没有更新 u 的情况下两者相等 那已经找到了最近公共祖先
    return u;

2. u和v一起更新 直到两者相等就返回

// 半伪代码
while (u != v) {
    u = u的父亲;
    v = v的父亲;
}
return u;

太慢了!!!

u 或者 v一层一层上去可没时间。

极端情况下就是这样:

每次处理数据都需要进行一次,假设q = 数据最大值,循环次数就是(2 * 100000) ^ 2 = 40,000,000,000。

使用倍增法优化

我们把u,v的第x个祖先看成2 ^ x + 2 ^ y + 2 ^ z...

int f[N][20]; // f[x][i] 节点 x 的 第2^i 个祖先(从下往上)

DFS中记录下任意节点的第2 ^ i个祖先,如下(包含原本的代码)。

void dfs(int fa, int x) {
    dist[x] = dist[fa] + 1;
    f[x][0] = fa; // x的第2^0(1)个祖先就是它爹
    for (int i = 1; i <= 18; i++)
        f[x][i] = f[f[x][i - 1]][i - 1]; // x的第2^i个祖先就是 它2^(i-1)个祖先的第2^(i-1)个祖先 因为2^i = 2^(i-1) + 2^(i-1)
    for (int u : e[x]) {
        if (u != fa)
            dfs(x, u);
    }
}

LCA中也要改动。

int lca(int u, int v) {
    // 1
    if (dist[u] > dist[v])
        swap(u, v);
    for (int i = 18; i >= 0; i--) { // 2^18 > 2*10^5
        if (dist[u] <= dist[f[v][i]])
            v = f[v][i];
        if (u == v) return u;
    }

    //2
    for (int i = 18; i >= 0; i--) {
        if (f[u][i] != f[v][i])
            u = f[u][i], v = f[v][i];
    }
    return f[u][0]; // u和v最后是它们公共祖先的两个儿子 所以它们公共祖先是它们任意一个的父亲
}

块1:i从18开始,试探v的第2^i个祖先 是否 存在 并 深度大于等于u,如果满足以上条件就把v变成v的第2^i个祖先,然后i = 17,16...继续试探,直到u == v或i == 0。

块2:i同样从18开始,试探v的第2^i个祖先 是否和 u的第2^i个祖先不相等,如果不相等,就更新u和v(同上),循环结束后,u和v一定是它们最近公共祖先的两个儿子,最后返回它们任意一个的父亲。

四、完整代码

长度有点小小的震撼,但相信你已经看懂了。

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 200005;

vector<int> e[N];
int n, k, q, door[N]; // door[] 存放每个传送点
int dist[N] = {-1}, f[N][20]; // dist[] 每个点离根节点的距离 f[x][i] 节点 x 的 第2^i 个祖先(从下往上)
int dst[N]; // dst[i] = i 到 离i最近的传送点 的距离

void bfs() {
    memset(dst, 0x3f, sizeof (dst));
    queue<int> qu;
    for (int i = 1; i <= k; i++) {
        qu.push(door[i]); // 存入所有的传送点
        dst[door[i]] = 0;
    }
    while (!qu.empty()) {
        int now = qu.front();
        qu.pop();
        for (int x : e[now]) {
            if (dst[x] == 0x3f3f3f3f) {
                dst[x] = dst[now] + 1; // 更新x离最近传送点的位置
                qu.push(x);
            }
        }
    }
}


void dfs(int fa, int x) {
    dist[x] = dist[fa] + 1;
    f[x][0] = fa;
    for (int i = 1; i <= 18; i++)
        f[x][i] = f[f[x][i - 1]][i - 1];
    for (int u : e[x]) {
        if (u != fa)
            dfs(x, u);
    }
}


int lca(int u, int v) {
    if (dist[u] > dist[v])
        swap(u, v);
    for (int i = 18; i >= 0; i--) {
        if (dist[u] <= dist[f[v][i]])
            v = f[v][i];
        if (u == v) return u;
    }

    for (int i = 18; i >= 0; i--) {
        if (f[u][i] != f[v][i])
            u = f[u][i], v = f[v][i];
    }
    return f[u][0];
}


int solve(int u, int v) {
    // ** 使用传送门
    int res1 = dst[u] + dst[v]; // 找离起点最近的传送点 传送至 离终点最近的传送点
    // **不使用传送门
    int res2 = dist[u] + dist[v] - 2 * dist[lca(u, v)];
    return min(res1, res2); // 取小者
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n >> k >> q;
    for (int i = 1; i <= n - 1; i++) {
        int x, y;
        cin >> x >> y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for (int i = 1; i <= k; i++) 
        cin >> door[i];
    
    bfs();
    dfs(0, 1);
    while (q--) {
        int u, v;
        cin >> u >> v;
        cout << solve(u, v) << endl;
    }
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/962926.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

本地部署DeepSeekp R1教程

目录 一.打开ollama官网&#xff0c;下载安装 1.下载完成双击安装程序 2.winr 输入cmd打开命令行输入命令 查看是否安装成功 二.部署DeepSeek R1模型 1. 下载模型&#xff1a;终端输入 (根据你的显存大小选择版本&#xff0c;16g就可以选择14b/32b)**电脑配置很低的话选…

OVS-DPDK

dpdk介绍及应用 DPDK介绍 DPDK&#xff08;Data Plane Development Kit&#xff09;是一组快速处理数据包的开发平台及接口。有intel主导开发&#xff0c;主要基于Linux系统&#xff0c;用于快速数据包处理的函 数库与驱动集合&#xff0c;可以极大提高数据处理性能和吞吐量&…

87.(3)攻防世界 web simple_php

之前做过&#xff0c;回顾 12&#xff0c;攻防世界simple_php-CSDN博客 进入靶场 <?php // 显示当前 PHP 文件的源代码&#xff0c;方便调试或查看代码结构 // __FILE__ 是 PHP 的一个魔术常量&#xff0c;代表当前文件的完整路径和文件名 show_source(__FILE__);// 包含…

物联网 STM32【源代码形式-ESP8266透传】连接OneNet IOT从云产品开发到底层MQTT实现,APP控制 【保姆级零基础搭建】

一、MQTT介绍 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输协议&#xff09;是一种基于发布/订阅模式的轻量级通讯协议&#xff0c;构建于TCP/IP协议之上。它最初由IBM在1999年发布&#xff0c;主要用于在硬件性能受限和网络状况不佳的情…

Airflow:深入理解Apache Airflow Task

Apache Airflow是一个开源工作流管理平台&#xff0c;支持以编程方式编写、调度和监控工作流。由于其灵活性、可扩展性和强大的社区支持&#xff0c;它已迅速成为编排复杂数据管道的首选工具。在这篇博文中&#xff0c;我们将深入研究Apache Airflow 中的任务概念&#xff0c;探…

【数据结构篇】时间复杂度

一.数据结构前言 1.1 数据结构的概念 数据结构(Data Structure)是计算机存储、组织数据的⽅式&#xff0c;指相互之间存在⼀种或多种特定关系的数 据元素的集合。没有⼀种单⼀的数据结构对所有⽤途都有⽤&#xff0c;所以我们要学各式各样的数据结构&#xff0c; 如&#xff1a…

Kafka 副本机制(包含AR、ISR、OSR、HW 和 LEO 介绍)

文章目录 Kafka 副本机制&#xff08;包含AR、ISR、OSR、HW 和 LEO 介绍&#xff09;1. 副本的基本概念2. 副本同步和一致性2.1 AR&#xff08;Assigned Replicas&#xff09;2.2 ISR&#xff08;In-Sync Replicas&#xff09;2.3 OSR&#xff08;Out-of-Sync Replicas&#xf…

完美还是完成?把握好度,辨证看待

完美还是完成&#xff1f; 如果说之前这个答案有争议&#xff0c;那么现在&#xff0c;答案毋庸置疑 ■为什么完美大于完成 ●时间成本&#xff1a; 做事不仅要考虑结果&#xff0c;还要考虑时间和精力&#xff0c;要说十年磨一剑的确质量更好&#xff0c;但是现实没有那么多…

Kafka中文文档

文章来源&#xff1a;https://kafka.cadn.net.cn 什么是事件流式处理&#xff1f; 事件流是人体中枢神经系统的数字等价物。它是 为“永远在线”的世界奠定技术基础&#xff0c;在这个世界里&#xff0c;企业越来越多地使用软件定义 和 automated&#xff0c;而软件的用户更…

电磁波谱与图像

我们所处的世界&#xff0c;其实是被各种各样的电磁波所包围的&#xff0c;从我们能看到的可见光&#xff0c;到不可见的红外&#xff0c;以及紫外&#xff0c;X&#xff0c;Gamma 射线&#xff0c;还有信息传输中的无线电波&#xff0c;雷达波&#xff0c;都属于电磁波。 引用…

海外问卷调查之渠道查,对企业经营的重要价值有哪些表现

海外问卷调查&#xff0c;是市场研究的重要手段之一&#xff0c;而市场研究的定义为&#xff1a;针对企业和机构进行的信息收集和研究过程&#xff0c;将企业和机构需要的信息具体化&#xff0c;同时设计合理的信息收集方法&#xff0c;管理并实施信息的收集过程&#xff0c;并…

LabVIEW无线齿轮监测系统

本案例介绍了基于LabVIEW的无线齿轮监测系统设计。该系统利用LabVIEW编程语言和改进的天牛须算法优化支持向量机&#xff0c;实现了无线齿轮故障监测。通过LabVIEW软件和相关硬件&#xff0c;可以实现对齿轮箱振动信号的采集、传输和故障识别&#xff0c;集远程采集、数据库存储…

(三)QT——信号与槽机制——计数器程序

目录 前言 信号&#xff08;Signal&#xff09;与槽&#xff08;Slot&#xff09;的定义 一、系统自带的信号和槽 二、自定义信号和槽 三、信号和槽的扩展 四、Lambda 表达式 总结 前言 信号与槽机制是 Qt 中的一种重要的通信机制&#xff0c;用于不同对象之间的事件响…

如何为用户设置密码

[rootxxx ~]# passwd aa #交互式的为用户设置密码 或者 [rootxxx ~]# echo 123 | passwd --stdin aa #不交互式的为用户设置密码 &#xff08;适用于批量的为用户更改密码&#xff0c;比如一次性为100个用户初始化密码&#xff09;

4 Hadoop 面试真题

4 Hadoop 面试真题 1. Apache Hadoop 3.0.02. HDFS 3.x 数据存储新特性-纠删码Hadoop面试真题 1. Apache Hadoop 3.0.0 Apache Hadoop 3.0.0在以前的主要发行版本&#xff08;hadoop-2.x&#xff09;上进行了许多重大改进。 最低要求的Java版本从Java 7增加到Java 8 现在&…

吴恩达深度学习——优化神经网络

本文来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习所用。 文章目录 优化样本大小mini-batch 优化梯度下降法动量梯度下降法指数加权平均概念偏差纠正 动量梯度下降法 RMSpropAdam优化算法 优化学习率局部最优问题&#xff08;了解&#xff09; 优…

Google Chrome-便携增强版[解压即用]

Google Chrome-便携增强版 链接&#xff1a;https://pan.xunlei.com/s/VOI0OyrhUx3biEbFgJyLl-Z8A1?pwdf5qa# a 特点描述 √ 无升级、便携式、绿色免安装&#xff0c;即可以覆盖更新又能解压使用&#xff01; √ 此增强版&#xff0c;支持右键解压使用 √ 加入Chrome增强…

Java篇之继承

目录 一. 继承 1. 为什么需要继承 2. 继承的概念 3. 继承的语法 4. 访问父类成员 4.1 子类中访问父类的成员变量 4.2 子类中访问父类的成员方法 5. super关键字 6. super和this关键字 7. 子类构造方法 8. 代码块的执行顺序 9. protected访问修饰限定符 10. 继承方式…

sobel边缘检测算法

人工智能例子汇总&#xff1a;AI常见的算法和例子-CSDN博客 Sobel边缘检测算法是一种用于图像处理中的边缘检测方法&#xff0c;它能够突出图像中灰度变化剧烈的地方&#xff0c;也就是边缘。该算法通过计算图像在水平方向和垂直方向上的梯度来检测边缘&#xff0c;梯度值越大…

MySQL为什么默认引擎是InnoDB ?

大家好&#xff0c;我是锋哥。今天分享关于【MySQL为什么默认引擎是InnoDB &#xff1f;】面试题。希望对大家有帮助&#xff1b; MySQL为什么默认引擎是InnoDB &#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 MySQL 默认引擎是 InnoDB&#xff0c;主要…