树上子链
题目描述
给定一棵树 T ,树 T 上每个点都有一个权值。
定义一颗树的子链的大小为:这个子链上所有结点的权值和。
请在树 T 中找出一条最大的子链并输出。
输入描述:
第一行输入一个 n , 1 ≤ n ≤ 1 0 5 n,1 \le n \le 10^5 n,1≤n≤105 。
接下来一行包含 n n n 个数,对于每个数 a i , − 1 0 5 ≤ a i ≤ 1 0 5 a_i, -10^5 \le a_i \le 10^5 ai,−105≤ai≤105 ,表示 i i i 结点的权值。
接下来有 n-1 行,每一行包含两个数 u , v ( 1 ≤ u , v ≤ n , u ! = v ) u,v(1 \le u,v \le n, u != v) u,v(1≤u,v≤n,u!=v),表示 u u u 与 v v v 之间有一条边。
输出描述:
仅包含一个数,表示我们所需要的答案。
输入
5
2 -1 -1 -2 3
1 2
2 3
2 4
2 5
输出
4
说明
样例中最大子链为 1 -> 2 -> 5
备注:
一个结点,也可以称作一条链
思路:
如果这个题没有点的权值,那就是求树的直径。现在多了点的权值,求最长链的大小,其实差不多。
d
p
u
dp_u
dpu表示以
u
u
u 为根的子树中的最长链,答案肯定是以某个点为根的两条子树最长链的和(包括根)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const ll INFF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 1e9 + 7;
const int N = 1e5 + 5;
const int M = 1e6 + 5;
int n, a[N];
ll dp[N];
vector<int> e[N];
ll ans = -INFF;
void dfs(int u, int fa) {
dp[u] = a[u];
for (int v : e[u]) {
if (v == fa) continue;
dfs(v, u);
ans = max(ans, dp[u] + dp[v]); //这里是关键,求了经过u的两条子树上的子链的和的最大值
dp[u] = max(dp[u], a[u] + dp[v]); //dp[u]表示u到它子树上的一点的最大值
// 这两句的顺序不能变,注意理解
}
ans = max(ans, dp[u]); //最后一个子节点更新
}
int main() {
#ifdef LZH_LOCAL
freopen("in.in", "r", stdin);
// freopen("out.out", "w", stdout);
#endif
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i < n; i++) {
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1, 0);
cout << ans << endl;
return 0;
}