NC202475 树上子链
题目
题目描述
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和 。
请在树 T 中找出一条最大的子链并输出。
输入描述
第一行输入一个 \(n,1 \le n \le 10^5\) 。
接下来一行包含n个数,对于每个数 \(a_i, -10^5 \le a_i \le 10^5\) ,表示 i 结点的权值。
接下来有n-1行,每一行包含两个数u,v( \(1 \le u,v \le n\), u != v),表示u与v之间有一条边。
输出描述
仅包含一个数,表示我们所需要的答案。
示例1
输入
5
2 -1 -1 -2 3
1 2
2 3
2 4
2 5
输出
4
说明
样例中最大子链为1 -> 2 -> 5
备注
一个结点,也可以称作一条链
题解
知识点:树形dp。
这道题是树的直径的变种题,树上权值最大链。
考虑以 \(1\) 为根,设 \(dp[u]\) 为以 \(u\) 为根的子树中过 \(u\) 的最大单链(指只沿着 \(u\) 的一个子结点方向扩展,而不是分成两个子节点)。转移方程为:\(dp[u] = \max(dp[u],dp[v_i]+a[u])\)。
一颗子树 \(u\) 的最大直径,可以通过其子节点的子树 \(v_i\) 的最大直径的最大值,以及过自己点形成的链的最大值得到。而后者通过过 \(u\) 的最长链加次长链得到。对于前者,我们只要求最大的即可,所以可以通过 \(ans\) 记录目前为止最大直径,即可满足前者要求。后者的最长链加次长链有两种方法:
- 记录一次遍历子节点中的最长链 \(d_1\) 和次长链 \(d_2\) ,最后 \(\max (ans,d_1+d_2)\) 即可。
- 可以通过在求 \(dp[u]\) 的过程中得到,而不用两个变量记录。首先 \(dp[u]\) 一定会经过最长链并且记录它,其次在其他情况必定会经过次长链,因此可以通过 \(\max (ans,dp[u] + dp[v])\) 来表示目前为止过 \(u\) 的最长链加上当前子节点的最长链的和。如果最长链在次长链之前,则显然可以;如果之后,则在遇到最长链之前一定是次长链最长,最后一定会得到次长链加最长链的情况,所以这种方法可行。需要注意的是,这步操作要在这次求 \(dp[u]\) 之前完成,因为 \(dp[u]\) 的更新包括了 \(dp[v]\),如果放在这之后,会有可能加两次 \(dp[v]\) ,然而这是不合法的。
个人觉得第二种求的方式比较方便。
扩展1:根节点确定,求树中各个子树的直径,只需要每次遍历子节点之后把最终ans记录下就行。
扩展2:求树中过每个子节点的最长链,这个需要求最长单链时记录最长单链和次长单链以及对应的节点,因为有可能该节点就在父节点的最长单链上,需要和父节点的次长单链结合,总体就是换根dp一下。
时间复杂度 \(O(n)\)
空间复杂度 \(O(n)\)
代码
方法一
#include <bits/stdc++.h>
#define ll long long
using namespace std;
vector<int> g[100007];
int a[100007];
ll dp[100007];///以u向下的单链最大权值
ll ans = -1e18;///初始化负无穷
///如果求任意子树的直径,要在这个基础上再加个f[u],表示以u为根的子树的最大直径
///从f[v],d1+d2+a[u]转移,表示子树的最大直径和过u的直径取最大值
int dfs(int u, int fa) {
ll d1 = 0, d2 = 0;///子最长,子次长,因为可以不选所以初始为0
dp[u] = a[u];///初始化
for (auto v : g[u]) {
if (v == fa) continue;
dfs(v, u);
dp[u] = max(dp[u], a[u] + dp[v]);///更新过u单链最大权值
if (dp[v] > d1) d2 = d1, d1 = dp[v];
else if (dp[v] > d2) d2 = dp[v];
}
ans = max(ans, d1 + d2 + a[u]);
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i < n;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
cout << ans << '\n';
return 0;
}
方法二
#include <bits/stdc++.h>
#define ll long long
using namespace std;
vector<int> g[100007];
int a[100007];
ll dp[100007];///以u向下的单链最大权值
ll dfs(int u, int fa) {
dp[u] = a[u];///过u初始化
ll ans = a[u];///初始化单点,防止无孩子
for (auto v : g[u]) {
if (v == fa) continue;
ans = max(ans, dfs(v, u));///子树最大值(不一定过子节点)
ans = max(ans, dp[u] + dp[v]);///尝试过u最大值 = u最长单链(u+子最长子链) + 子次长子链
dp[u] = max(dp[u], a[u] + dp[v]);///更新过u单链最大权值,要在答案更新下面,不然会加两次同样的值
}
return ans;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n;i++) cin >> a[i];
for (int i = 1;i < n;i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
cout << dfs(1, 0) << '\n';
return 0;
}
NC202475 树上子链的更多相关文章
- 小白月赛22 B : 树上子链
B:树上子链 考察点 : 树的直径 坑点 : long long, 是点权不是边权 一个点也算一条链 析题得侃: 关于树的直径 这道题考察的是树的直径,最好用树形DP来写,具体解释详见上述博客, 这道 ...
- 牛客小白月赛6 C 桃花 dfs 求树上最长直径
链接:https://www.nowcoder.com/acm/contest/136/C来源:牛客网 题目描述 桃花一簇开无主,可爱深红映浅红. ...
- BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]
2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MBSubmit: 5217 Solved: 1233 ...
- BZOJ 3784: 树上的路径
Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...
- HDU 2376 树形dp|树上任意两点距离和的平均值
原题:http://acm.hdu.edu.cn/showproblem.php?pid=2376 经典问题,求的是树上任意两点和的平均值. 这里我们不能枚举点,这样n^2的复杂度.我们可以枚举每一条 ...
- LCA + 树状数组 + 树上RMQ
题目链接:http://poj.org/problem?id=2763 思路:首先求出树上dfs序列,并且标记树上每个节点开始遍历以及最后回溯遍历到的时间戳,由于需要修改树上的某两个节点之间的权值,如 ...
- HDU 2545 树上战争 (并查集+YY)
题意:给一棵树,如果树上的某个节点被某个人占据,则它的所有儿子都被占据,lxh和pfz初始时分别站在两个节点上,lxh总是先移动 ,谁当前所在的点被另一个人占据,他就输了比赛,问谁能获胜 比较有意思的 ...
- poj1155 TELE (树上的背包)
题目链接:http://poj.org/problem?id=1155 题意:给定一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值.现在要在电视台 ...
- Codevs 2370 小机房的树 LCA 树上倍增
题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...
随机推荐
- VMware虚拟机基于contos 7 搭建lnmp环境全过程
这个环境也整了几次了,由于本人比较懒,没有记住.找资料很麻烦,就自己动手咯 1.下载VMware虚拟机 (有注册码) 地址:http://www.zdfans.com/5928.html ...
- 在Visual C++ 6.0中无法使用gets()函数的解决办法
问题 昨晚遇到一个有意思的问题,明明在Visual Studio 2019运行好好的C语言代码,Copy到Visual C++ 6.0中就无法编译通过了,错误提示信息如下: error C2143: ...
- 2 Elment Ui 日期选择器 格式化问题
Elment Ui 日期选择器 格式化问题 在前后端联调过程中 我们常常会遇到日期无法被反序列化 这个问题 会有点头疼 下面以我这边为例 后端使用的是springboot 默认集成了jackjson ...
- 聊聊C#中的composite模式
写在前面 Composite组合模式属于设计模式中比较热门的一个,相信大家对它一定不像对访问者模式那么陌生,毕竟谁又没有遇到过树形结构呢.不过所谓温故而知新,我们还是从一个例子出发,起底一下这个模式吧 ...
- mysql5.7安装要踩的坑
因为官网下载的是绿色版,所以要做一些配置 1.在mysql根目录新增data文件夹和my.ini文件 my.ini文件内容 [mysql]# 设置mysql客户端默认字符集default-charac ...
- JAVA中计算两个日期时间的差值竟然也有这么多门道
上半年春招的时候,作为面试官,对于面试表现的不错的同学会要求其写一小段代码看看.题目很简单: 给定一个日期,然后计算下距离今天相差的天数. 本以为这么个问题就是用来活跃面试氛围的,但是结果却让人大跌眼 ...
- abstract,抽象修饰符
//abstract 抽象类:类由extends继承继承表现在单继承(接口可以多继承)//abstract--约束~~有人帮我们实现抽象方法,只有方法名字,没有方法实现1.不能靠new这个抽象类,只靠 ...
- Linux教我们丢掉键盘
前言 本篇博客并非博主编写,而是跟着黑马教程整理的. 因为Linux都是一些操作命令,也不想深入研究,所以就用此博客记录一下平时的基本知识,方便日后查找. 一.常用命令 命令 对应英文 作用 ls l ...
- 2022徐特立科学营&BIT机器人队电控课程讲义
目录 \(\cdot\)电控简介 \(\cdot\)认识单片机 什么是单片机 时钟-单片机的脉搏 \(\cdot\)外设及应用 GPIO PWM 定时器 UART \(\cdo ...
- DeiT:注意力也能蒸馏
DeiT:注意力也能蒸馏 <Training data-efficient image transformers & distillation through attention> ...