给出一棵树,n 个点 , n-1 条边。点有点权,让你给出树上的两条路径,使得两条路径的不相交的所有的点的点权和最大。
手玩可以得到,一个比较明显的结论就是:相交的点至多为 1。
那么就可以分成两种情况。
1、有一个点相交的情况,这种情况可以看做由根节点发出的 4 条链,可以换根 dp 做。换根 dp 维护由 点 u 发出的所有的链 chain(u) ,然后按照链的长度排序,取前 4 个即可。
2、没有点相交的情况。此种情况可以看做选择一条边断开之后,分成了两棵树,在两棵树中找到带边权的直径。此种情况换根 dp 不好做,于是借鉴了网上的方法。
记 F(u,v) 表示在以 u 为根的树中,钦定删去临界点 v 的子树之后树的直径,那么答案为:
max(u,v)∈TF(u,v)+F(v,u)
然后考虑如何处理 F。
不妨对于 F(u,v),钦定 v 为 u 的父节点,考虑记忆化搜索。首先根据第一种情况预处理的 chain(u),从中找出不以 v 为端点的最长和次长链更新经过该节点的路径的贡献,然后枚举其子节点考虑其子树中路径的贡献即可。
#include <map>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define N 200010
#define mp make_pair
using namespace std;
int n, m, add_edge, ans;
int head[N], a[N];
vector<pair<int, int> > max1[N];
map<int, int> ma[N];
struct node {
int next, to;
}edge[N << 1];
void add(int from, int to) {
edge[++add_edge].next = head[from];
edge[add_edge].to = to;
head[from] = add_edge;
}
void dfs(int x, int fa) {
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == fa) continue;
dfs(to, x);
max1[x].push_back(mp(a[to], to));
if (!max1[to].empty()) max1[x].back().first += max1[to][0].first;
}
sort(max1[x].begin(), max1[x].end(), greater< pair<int, int> >());
}
void dfs2(int x, int fa, int dis) {
if (fa) {
max1[x].push_back(mp(dis, fa));
sort(max1[x].begin(), max1[x].end(), greater <pair <int, int> >());
}
while (max1[x].size() < 4) max1[x].push_back(mp(0, 0));
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == fa) continue;
if (to != max1[x][0].second) dfs2(to, x, max1[x][0].first + a[x]);
else dfs2(to, x, max1[x][1].first + a[x]);
}
}
int F(int x, int fa) {
if (ma[x].count(fa)) return ma[x][fa];
int p1 = 0;
while (max1[x][p1].second == fa) p1++;
int p2 = p1 + 1;
while (max1[x][p2].second == fa) p2++;
int ret = max1[x][p1].first + max1[x][p2].first + a[x];
for (int i = head[x]; i; i = edge[i].next) {
int to = edge[i].to;
if (to == fa) continue;
ret = max(ret, F(to, x));
}
ma[x][fa] = ret;
return ret;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1, x, y; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
dfs(1, 0), dfs2(1, 0, 0);
for (int i = 1; i <= n; i++) {
int sum = 0;
for (int j = 0; j < 4; j++)
sum += max1[i][j].first;
ans = max(ans, sum);
}
for (int x = 1; x <= n; x++)
for (int i = head[x]; i; i = edge[i].next) {
ans = max(ans, F(x, edge[i].to) + F(edge[i].to, x));
}
printf("%lld\n", ans);
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)