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

题解 - 树上游走(二)(上海月赛2024.7甲组T1)

题目描述

给定一个棵由编号为 1~n 这 n 个节点构成的树,及 m 个关键节点,树的第 i 条双向边连接 ui 和 vi ,长度为 wi 。你可以从树上任何一个节点出发开始游走,到任何一个节点结束,但整个游走过程中必须经过给定的 m 个关键节点。
请问,应当如何设计路线,能够使得整个游走过程中,经过的路径长度最短。

输入格式

输入第一行,两个正整数 n,m
接下来 n−1 行,每行 3 个正整数,其中第 i+1 行分别表示第 i 条边的 ui,vi,wi。
输入最后行,m 个正整数,分别表示 m 个关键节点的编号 ci。

输出格式

输出共一行,表示所求最短长度。

数据范围

1≤m≤n≤1e5,1​ ≤ ui,vi,ci ≤n, 1≤w ≤1e4

样例数据

输入:

5 3
1 2 2
2 3 4
2 4 5
1 5 1
3 4 5

输出:

15

说明:

3–>2–>1–>5–>1–>2–>4
4 2 1 1 2 5

分析

树形dp + 换根

  1. 首先考虑对于起点固定,应该怎么做。

假设起点为st,假设m个关键节点分布在st的k个子树内,则我们将只经过其中1个子树一次,然后其余k-1个子树的每条边经过两次。

一个很显然的贪心是,为了使总路程最短,我们应该满足 只经过1次的子树的路程和 最大。

可以dfs来求

其中f[u]代表从u节点访问u子树的所有关键点然后再返回u节点需要的总路程(即所有路径均为两倍),
mx[i][0]和mx[i][1]分别表示i的子树的最大值和次大值。(至于为什么还需要维护次大值,后续会讲到)

void dfs(int u,int fa){if(flag[u]) cnt[u] = 1;for(int i = h[u];i != -1;i = ne[i]){int j = e[i],val = w[i];if(j != fa){dfs(j,u);if(cnt[j]){cnt[u] += cnt[j];f[u] += f[j] + 2 * val;int tmp = mx[j][0] + val;if(tmp > mx[u][0]){mx[u][1] = mx[u][0],id[u][1] = id[u][0];mx[u][0] = tmp,id[u][0] = j;}else if(tmp > mx[u][1]){mx[u][1] = tmp,id[u][1] = j;}} }}
}

最终结果即为 f[st] - mx[st][0]

  1. 然后考虑起点不固定

可以采用换根dp来写

对于 u → j u \rightarrow j uj 这条边,根从 u u u 转移到 j j j

若 j 子树内关键节点数为0,则 f[j] = f[u] + 2 * val
若 j 子树内关键节点数为m,则 f[j] 不变;
否则, f[j] = f[u]

若 j 子树内关键节点数为m,则还需要更新mx[j][0]和mx[j][1]的值。(具体细节见代码)

void dp(int u,int fa){res = min(res,f[u] - mx[u][0]);for(int i = h[u];i != -1;i = ne[i]){int j = e[i],val = w[i];if(j != fa){if(!cnt[j]) f[j] = f[u] + 2 * val;else if(cnt[j] == m) f[j] = f[j];else f[j] = f[u];if(cnt[j] != m){int tmp;if(id[u][0] == j) tmp = mx[u][1] + val;else tmp = mx[u][0] + val;if(tmp > mx[j][0]){mx[j][1] = mx[j][0],id[j][1] = id[j][0];mx[j][0] = tmp,id[j][0] = u;}else if(tmp > mx[j][1]){mx[j][1] = tmp,id[j][1] = u;}}dp(j,u);}}
}

完整代码

#include<bits/stdc++.h>using namespace std;typedef long long LL;const int N = 1e5 + 10;int n,m;
bool flag[N];
int h[N],e[N * 2],w[N * 2],ne[N * 2],idx;
LL cnt[N],f[N],mx[N][2],id[N][2],res;void add(int a,int b,int c){e[idx] = b,w[idx] = c,ne[idx] = h[a],h[a] = idx++;
}void dfs(int u,int fa){if(flag[u]) cnt[u] = 1;for(int i = h[u];i != -1;i = ne[i]){int j = e[i],val = w[i];if(j != fa){dfs(j,u);if(cnt[j]){cnt[u] += cnt[j];f[u] += f[j] + 2 * val;int tmp = mx[j][0] + val;if(tmp > mx[u][0]){mx[u][1] = mx[u][0],id[u][1] = id[u][0];mx[u][0] = tmp,id[u][0] = j;}else if(tmp > mx[u][1]){mx[u][1] = tmp,id[u][1] = j;}} }}
}void dp(int u,int fa){res = min(res,f[u] - mx[u][0]);for(int i = h[u];i != -1;i = ne[i]){int j = e[i],val = w[i];if(j != fa){if(!cnt[j]) f[j] = f[u] + 2 * val;else if(cnt[j] == m) f[j] = f[j];else f[j] = f[u];if(cnt[j] != m){int tmp;if(id[u][0] == j) tmp = mx[u][1] + val;else tmp = mx[u][0] + val;if(tmp > mx[j][0]){mx[j][1] = mx[j][0],id[j][1] = id[j][0];mx[j][0] = tmp,id[j][0] = u;}else if(tmp > mx[j][1]){mx[j][1] = tmp,id[j][1] = u;}}dp(j,u);}}
}int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);memset(h,-1,sizeof h);cin >> n >> m;for(int i = 0,u,v,w;i < n - 1;i++){cin >> u >> v >> w;add(u,v,w),add(v,u,w);}for(int i = 1,j;i <= m;i++){cin >> j;flag[j] = true;}dfs(1,-1);res = f[1] - mx[1][0];dp(1,-1);cout << res;return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python(模块)
  • 微信小程序实现上传照片功能
  • C#加班统计次数
  • CSS:图片间空白间距问题的解决方案
  • java Path对象和URI对象的转换
  • Python的并行任务(进程池、线程池)
  • 关于vs2022项目占用空间太大的问题
  • MongoDB未授权访问漏洞
  • 【selenium】文件上传、下载、读取
  • TF卡(SD NAND)参考设计和使用提示
  • Codeforces Round 963 (Div. 2)
  • 【Git企业级开发实战指南①】Git安装、基本操作!
  • 文件加密软件精品推荐(10款不容错过的文件加密软件)
  • 【Unity】 HTFramework框架(五十四)【进阶篇】Deployment 轻量级资源部署管线
  • VUE框架面试整理-Vuex
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • 230. Kth Smallest Element in a BST
  • 77. Combinations
  • canvas 绘制双线技巧
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Vue.js 移动端适配之 vw 解决方案
  • 从0实现一个tiny react(三)生命周期
  • 免费小说阅读小程序
  • 前端路由实现-history
  • 原生JS动态加载JS、CSS文件及代码脚本
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​​​​​​​GitLab 之 GitLab-Runner 安装,配置与问题汇总
  • ​软考-高级-系统架构设计师教程(清华第2版)【第12章 信息系统架构设计理论与实践(P420~465)-思维导图】​
  • ​一、什么是射频识别?二、射频识别系统组成及工作原理三、射频识别系统分类四、RFID与物联网​
  • #APPINVENTOR学习记录
  • #etcd#安装时出错
  • #数据结构 笔记一
  • (层次遍历)104. 二叉树的最大深度
  • (第三期)书生大模型实战营——InternVL(冷笑话大师)部署微调实践
  • (附程序)AD采集中的10种经典软件滤波程序优缺点分析
  • (顺序)容器的好伴侣 --- 容器适配器
  • (四) Graphivz 颜色选择
  • (已解决)什么是vue导航守卫
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • *算法训练(leetcode)第四十七天 | 并查集理论基础、107. 寻找存在的路径
  • .gitignore不生效的解决方案
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .Net 基于.Net8开发的一个Asp.Net Core Webapi小型易用框架
  • .NET/C# 中设置当发生某个特定异常时进入断点(不借助 Visual Studio 的纯代码实现)
  • .NET分布式缓存Memcached从入门到实战
  • .NET开发人员必知的八个网站
  • .NET正则基础之——正则委托
  • .net中生成excel后调整宽度
  • @DependsOn:解析 Spring 中的依赖关系之艺术
  • @require_PUTNameError: name ‘require_PUT‘ is not defined 解决方法
  • @synthesize和@dynamic分别有什么作用?
  • [17]JAVAEE-HTTP协议
  • [AIGC] Redis基础命令集详细介绍