题解:P9245 [蓝桥杯 2023 省 B] 景区导游

news/2025/3/29 19:09:28/文章来源:https://www.cnblogs.com/tomorin/p/18794139

题意:

给出一个 \(n\) 节点的带权树和一个长度为 \(k\) 的原始路径 \(route\),要求对于路径中每个点 \(i\),求移去该点时所需的总代价。

思路:

注意到数据规模为 \(10^5\),因此当算法时间复杂度为 \(O(n\log n)\) 时可以解决问题。对于一个含 \(k\) 个路径点的路径,对每个路径点 \(i\),只需考虑两项:

● 相邻的 \(route[i-1]\)\(route[i]\) 的距离 \(cost[i]\)

● 跳过中间点(即 \(route[i-2]\)\(route[i]\))的距离 \(jump[i]\)

遍历每个路径点 \(route[i]\) 时,分三种情况计算总代价:

\[ans(i)=\begin{cases}\sum_{j=3}^{k} cost[j], &i=1,\\\sum_{j=2}^{k-1} cost[j], &i=k,\\\sum_{j=2}^{i-1} cost[j]+jump[i+1]+\sum_{j=i+2}^{k} cost[j], &2\le i\le k-1.\end{cases} \]

为了高效计算区间内 \(cost\) 的累加和,可构造前缀和数组 \(pre\)。由此查询区间和的时间复杂度为 \(O(1)\),而查询两点间距离的复杂度为 \(O(\log n)\),总体这部分的时间复杂度为 \(O(k\log n)\)

使用倍增求解公共祖先和距离,两点到公共祖先的距离和即为两点距离。可以选取任意节点作为根节点,这里我选取的是节点 \(1\)。通过 DFS 得到每个节点到根节点的距离,记:

\[dis[i]=\text{从节点 1 到节点 } i \text{ 的总距离} \]

同时构造倍增数组,其转移方程为:

\[fa[cur][i]=fa[fa[cur][i-1]][i-1] \]

对于任意两点 \(u\)\(v\),它们之间的距离为

\[dis[u]+dis[v]-2\cdot dis[LCA(u,v)] \]

其中 \(LCA(u,v)\)\(u\)\(v\) 的最近公共祖先。构造倍增数组的时间复杂度为 \(O(n\log n)\),而利用 LCA 查询两点距离的时间为 \(O(\log n)\)

因此,对于任意两点 \(x\)\(y\),可在 \(O(\log n)\) 内计算其距离,总体时间复杂度为 \(O((n+k)\log n)\),即可满足题目要求。

#include <iostream>
#include <algorithm>
#include <vector>
#define int long longusing namespace std;vector<pair<int, int>> e[100005];
int n, m, k;
int fa[100005][25] = { 0 };
int dep[100005] = { 0 };int route[100005] = { 0 };
int dis[100005] = { 0 };
int jump[100005] = { 0 };
int pre[100005] = { 0 };void dfs(int cur, int from) {fa[cur][0] = from;dep[cur] = dep[from] + 1;for (int i = 1; i <= 24; i++) {fa[cur][i] = fa[fa[cur][i - 1]][i - 1];}for (auto [u, w] : e[cur]) {if (u == from) continue;dis[u] = dis[cur] + w;dfs(u, cur);}
}int lca(int x, int y) {if (dep[x] > dep[y]) swap(x, y);int dis = dep[y] - dep[x];for (int i = 0; dis; i++, dis >>= 1) {if (dis & 1) y = fa[y][i];}if (x == y) return y;for (int i = 24; i >= 0; i--) {if (fa[x][i] != fa[y][i]) {x = fa[x][i], y = fa[y][i];}}return fa[y][0];
}signed main()
{cin.tie(0)->sync_with_stdio(0);cin >> n >> k;for (int i = 1; i <= n - 1; i++) {int u, v, w;cin >> u >> v >> w;e[u].push_back({ v,w });e[v].push_back({ u,w });}for (int i = 1; i <= k; i++) cin >> route[i];dfs(1, 1);//pre[i]表示从route[1]依次到route[i]的总花费,jump[i]表示从route[i-2]跳到route[i]的花费。for (int i = 2; i <= k; i++) {int x = route[i - 1], y = route[i];int fa = lca(x, y);pre[i] = pre[i - 1] + dis[x] + dis[y] - 2 * dis[fa];if (i + 1 <= k) {int z = route[i + 1];fa = lca(x, z);jump[i + 1] = dis[x] + dis[z] - 2 * dis[fa];}}for (int i = 1; i <= k; i++) {int ans = 0;if (i == 1) ans = pre[k] - pre[2];else if (i == k) ans = pre[k - 1];else ans = pre[i - 1] + jump[i + 1] + pre[k] - pre[i + 1];cout << ans << " ";}return 0;
}

关于优化(主要集中在LCA):

可以使用欧拉序列RMQ来 $ O(1) $ 查找父亲节点。因为本蒟蒻只会ST表RMQ,时间复杂度约为 $ O(n \log n + k) $;又因为该欧拉序列的大小是 $ 2n $,而 $ k\leq n $,因此反倒会因为常数较大比倍增慢。

欧拉序列+ST表RMQ:提交记录

朴素tarjan并查集能做到比倍增更优,但同欧拉序列一样,常数较大,不如重链剖分:提交记录

重链剖分可以把时间复杂度降到$ O(n + k\log n) $:提交记录

最后,不必构建前缀和数组。只需先求出完整走完的总花费,然后求移去某个节点后变化的花费即可。

以下是重链剖分且省去前缀和数组的AC代码,代码复杂度、时间复杂度和空间复杂度都较优:

#include <iostream>
#include <algorithm>
#include <vector>
#define int long longusing namespace std;vector<pair<int, int>> e[100005];
int n, m, k;
int dep[100005] = { 0 };
int fa[100005] = { 0 };
int siz[100005] = { 0 };
int son[100005] = { 0 };
int top[100005] = { 0 };int route[100005] = { 0 };
int dis[100005] = { 0 };
int jump[100005] = { 0 };
int to[100005] = { 0 };void dfs1(int cur) {siz[cur] = 1;for (auto [u, w] : e[cur]) {if (dep[u]) continue;dis[u] = dis[cur] + w;dep[u] = dep[cur] + 1;fa[u] = cur;dfs1(u);siz[cur] += siz[u];if (siz[u] > siz[son[cur]]) son[cur] = u;}
}void dfs2(int cur, int t) {top[cur] = t;if (!son[cur]) return;dfs2(son[cur], t);for (auto [u, w] : e[cur]) {if (u != son[cur] and u != fa[cur]) dfs2(u, u);}
}int lca(int x, int y) {while (top[x] != top[y]) {if (dep[top[x]] > dep[top[y]]) x = fa[top[x]];else y = fa[top[y]];}return dep[x] < dep[y] ? x : y;
}int getsum(int x, int y) {int fa = lca(x, y);return dis[x] + dis[y] - 2 * dis[fa];
}signed main()
{cin.tie(0)->sync_with_stdio(0);cin >> n >> k;for (int i = 1; i <= n - 1; i++) {int u, v, w;cin >> u >> v >> w;e[u].push_back({ v,w });e[v].push_back({ u,w });}for (int i = 1; i <= k; i++) cin >> route[i];dep[1] = 1;dfs1(1);dfs2(1, 1);int sum = 0;for (int i = 2; i <= k; i++) sum += to[i] = getsum(route[i - 1], route[i]);for (int i = 1; i <= k; i++) {int ans = 0;if (i == 1) ans = sum - to[i + 1];else if (i == k) ans = sum - to[i];else ans = sum - to[i] - to[i + 1] + getsum(route[i - 1], route[i + 1]);cout << ans << " ";}return 0;
}

最后,祝大家(还有我)蓝桥杯顺利......

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win11自带录屏工具

Setp1: 打开设置Setp2: 打开游戏->摄像Setp3: 配置录制参数Setp4: 开始录像 WIN+Alt+GSetp5: 打开录像文件

杭州储存卡数据恢复之雷克沙短路损坏不识别售后维修失败二次修复

这是一张64G的Lexar雷克沙CF接口内存卡,型号是1066X,CANON佳能5D4相机使用的,这张存储卡是硬件出问题了无法识别,说是可能插卡时角度不对用力顶入后使顶针VCC脚变形短路,通电后导致芯片损坏。客户先寄修到雷克沙官方售后那边进行维修,但最终检测修复失败,说是短路严重,…

RabbitMQ核心架构

Producer:负责产生消息。 Connection:RabbitMQ客户端和代理服务器之间的TCP连接。 Channel:建立在连接之上的虚拟连接,RabbitMQ操作都是在信道中进行。 Broker:一个Broker可以看做一个RabbitMQ服务节点或者服务实例。 Exchange:生产者发送消息到交换器,交换器根据路由ke…

024 登录页-main退出登录功能的实现

这个页面这样写一、 用于创建一个按钮并绑定一个点击事件处理函数。以下是对这段代码的详细解释:<button> 标签:这是 HTML 中的按钮元素,用于在页面上显示一个可点击的按钮。@click 指令:在 Vue.js 中,@click 是一个事件绑定指令,它用于监听按钮的点击事件。@ 是 …

ProfiNet转Modbus TCP协议转换网关驱动三菱PLC与伺服的毫秒级动态参数同步

一、案例背景 在“双碳”战略推动下,新能源锂电池行业迎来爆发式增长。某新能源科技公司新建的锂电池生产线中,涂布工序作为核心环节,采用了德国博世力士乐IndraDriveCX系列伺服驱动器(ProfiNet从站)实现高精度张力控制,而车间级监控系统选用三菱L系列PLC(ModbusTCP主站…

MySQL-面经

目录 MVCC概念?如何实现? 可重复读概念 可重复读下,快照是在什么时候生成的,是事务启动时,还是语句执行前 可重复读下,执行两个select语句,会生成几个快照?MVCC概念?如何实现? MVCC概念:通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制…

算法备案五大真相

一些开发者已经了解到算法备案是AI类产品必做的一项资质了,但因为经验有限,依然存在一些盲点和不清楚的地方。今天,我就整理出了最基础但也最重要的五大算法备案真相,供大家参考。如有其它疑问,欢迎进一步咨询算法备案办理问题。一、流程统一,审核不统一 算法备案有全国统…

Cknife配置

项目地址 https://github.com/Chora10/Cknife 使用Java编译器 这里使用eclipse 1. File->Open Project from File System...选择目录,点击完成2. 在刚添加的项目上按右键,并点击导出Export选择可运行的jar文件选择路径3. 这里没有选择或没有配置有效的“Launch configurat…

医疗场景实战:百条数据 RFT 微调盘古大模型,精度大幅提升

摘要:RFT强化微调是一种新型LLM微调方法,通过强化学习与传统微调结合,少量数据即可显著增强领域场景的模型能力。本文分享自华为云社区《医疗场景实战|百条数据RFT微调盘古大模型,效果超越DS》,作者:盘古大模型官方账号。 医疗场景实战|百条数据RFT微调盘古大模型,效果超…

SQL Server 启用 sa

Hello World ‍‍ ‍‍‍‍‍

一文看懂大数据生态圈完整知识体系

随着大数据行业的发展,大数据生态圈中相关的技术也在一直迭代进步,希望能通过本文帮助大家快速构建大数据生态圈的完整知识体系。 目前大数据生态圈中的核心技术总结下来如图1所示,分为以下9类,下面分别介绍。大数据生态下9类核心技术 01 数据采集技术框架 数据采集也被称为…

神秘另解集合,想出来一样的东西这辈子有了

P1600 考虑重链剖分。然后把每个路径给变成 \(O(\log n)\) 个重链,根据重链剖分的性质,每条重链的 dfs 序都为连续,所以把图画出来大概是像下图这样:横轴是时间,纵轴是 dfs 序。一个时间 \(t\) 在节点 \(p\) 的人数就是经过 \((t,\text{dfn}_p)\) 的线段数量。线段数量为 …