2022桂林G. Group Homework

1|0题目描述

给出一棵树,n 个点 , n-1 条边。点有点权,让你给出树上的两条路径,使得两条路径的不相交的所有的点的点权和最大。

2|0思路

手玩可以得到,一个比较明显的结论就是:相交的点至多为 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 为端点的最长和次长链更新经过该节点的路径的贡献,然后枚举其子节点考虑其子树中路径的贡献即可。

3|0code

#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__

本文作者Kersen
本文链接https://www.cnblogs.com/zzz-hhh/p/18130232.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Kersen  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示