题目链接

题目

题目描述

给定一棵树 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\) 记录目前为止最大直径,即可满足前者要求。后者的最长链加次长链有两种方法:

  1. 记录一次遍历子节点中的最长链 \(d_1\) 和次长链 \(d_2\) ,最后 \(\max (ans,d_1+d_2)\) 即可。
  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 树上子链的更多相关文章

  1. 小白月赛22 B : 树上子链

    B:树上子链 考察点 : 树的直径 坑点 : long long, 是点权不是边权 一个点也算一条链 析题得侃: 关于树的直径 这道题考察的是树的直径,最好用树形DP来写,具体解释详见上述博客, 这道 ...

  2. 牛客小白月赛6 C 桃花 dfs 求树上最长直径

    链接:https://www.nowcoder.com/acm/contest/136/C来源:牛客网 题目描述 桃花一簇开无主,可爱深红映浅红.                            ...

  3. 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 ...

  4. BZOJ 3784: 树上的路径

    Description 问一棵树上前 \(k\) 大路径的边权. Sol 边分治. 非常感谢数据没有菊花图. 为了写写边分治试试然后就开了这道题. 边分治非常好想,选一条重边,分成两部分,然后分别求最 ...

  5. HDU 2376 树形dp|树上任意两点距离和的平均值

    原题:http://acm.hdu.edu.cn/showproblem.php?pid=2376 经典问题,求的是树上任意两点和的平均值. 这里我们不能枚举点,这样n^2的复杂度.我们可以枚举每一条 ...

  6. LCA + 树状数组 + 树上RMQ

    题目链接:http://poj.org/problem?id=2763 思路:首先求出树上dfs序列,并且标记树上每个节点开始遍历以及最后回溯遍历到的时间戳,由于需要修改树上的某两个节点之间的权值,如 ...

  7. HDU 2545 树上战争 (并查集+YY)

    题意:给一棵树,如果树上的某个节点被某个人占据,则它的所有儿子都被占据,lxh和pfz初始时分别站在两个节点上,lxh总是先移动 ,谁当前所在的点被另一个人占据,他就输了比赛,问谁能获胜 比较有意思的 ...

  8. poj1155 TELE (树上的背包)

    题目链接:http://poj.org/problem?id=1155 题意:给定一棵树,1为根结点表示电视台,有m个叶子节点表示客户,有n-m-1个中间节点表示中转站,每条树边有权值.现在要在电视台 ...

  9. Codevs 2370 小机房的树 LCA 树上倍增

    题目描述 Description 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天,他们想爬到一个节点上去搞基,但是作为两只虫子, ...

随机推荐

  1. SQL Server 2019企业版和标准版的区别?

    来源公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485400&idx=1&a ...

  2. unittest框架里的常用断言方法:用于检查数据

    1.unittest框架里的常用断言方法:用于检查数据. (1)assertEqual(x,y) 检查两个参数类型相同并且值相等.(2)assertTrue(x) 检查唯一的参数值等于True(3)a ...

  3. STM32内存知识

    在了解STM32内存之前需要了解 MCU 的型号和MDK 中的.map 文件,很多刚学习 stm32 时都不会过多的去了解 MCU 的选型,是在太枯燥了.这里在从新了解一下,久了就熟悉了. 一.STM ...

  4. React关于constructor与super(props)之间的相爱相杀

    我们先把菜鸟教程的一段代码拿过来分析一下.下面这段代码是用了将生命周期方法添加到类中实现时钟效果. // 将生命周期方法添加到类中 class Clock extends React.Componen ...

  5. UiPath存在图像Image Exists的介绍和使用

    一.Image Exists的介绍 检查是否在指定的UI元素中找到图像,输出的是一个布尔值 二.Image Exists在UiPath中的使用 1. 打开设计器,在设计库中新建一个Sequence,为 ...

  6. UiPath文本操作Get Full Text的介绍和使用

    一.Get Full Text操作的介绍 使用Get Full Text(获取全文本 )屏幕抓取方法从指示的UI元素中提取字符串及其信息 二.Get Full Text在UiPath中的使用 1. 打 ...

  7. jenkins页面一直在Please wait while Jenkins is getting ready to work ...

    原因:因为访问官网太慢.我们只需要换一个源,不使用官网的源即可. 1.找到jenkins工作目录 find / -name *.UpdateCenter.xml 2.修改文件中的url,随后重启就行了 ...

  8. SQL Server数据库 备份A库,然后删除A库,再还原A库,此时数据库一直显示“正在还原”的解决方法

    SQL Server数据库 备份A库,然后删除A库,再还原A库,此时数据库一直显示"正在还原"的解决方法: A库一直显示"正在还原". 在这种状态下,由于未提交 ...

  9. String长度限制?

    String我们在开发和学习中会经常用到,但对String类型的取值范围我们并不明确. String底层是char数组,并未标明长度限制.java中可以对数组指定长度,如果不指定就以实际元素来指定 p ...

  10. Tomcat日志乱码解决方法

    将配置文件logging.properties所有含有UTF-8的删除