F. Gardening Friends
F. Gardening Friends
Two friends, Alisa and Yuki, planted a tree with vertices in their garden. A tree is an undirected graph without cycles, loops, or multiple edges. Each edge in this tree has a length of . Initially, vertex is the root of the tree.
Alisa and Yuki are growing the tree not just for fun, they want to sell it. The cost of the tree is defined as the maximum distance from the root to a vertex among all vertices of the tree. The distance between two vertices and is the sum of the lengths of the edges on the path from to .
The girls took a course in gardening, so they know how to modify the tree. Alisa and Yuki can spend coins to shift the root of the tree to one of the neighbors of the current root. This operation can be performed any number of times (possibly zero). Note that the structure of the tree is left unchanged; the only change is which vertex is the root.
The friends want to sell the tree with the maximum profit. The profit is defined as the difference between the cost of the tree and the total cost of operations. The profit is cost of the tree minus the total cost of operations.
Help the girls and find the maximum profit they can get by applying operations to the tree any number of times (possibly zero).
Input
The first line of the input contains one integer () — the number of test cases.
The description of the test cases follows.
The first line of each test case contains integers , , (; ) — the number of vertices in the tree, the length of each edge, and the cost of the operation.
The next lines of the test case contain pairs of integers , () — the edges of the graph. These edges form a tree.
The sum of the values of over all test cases does not exceed .
Output
For each test case, output a single integer — the maximum profit that Yuki and Alisa can get.
Example
input
4 3 2 3 2 1 3 1 5 4 1 2 1 4 2 5 4 3 4 6 5 3 4 1 6 1 2 6 5 1 3 2 10 6 4 1 3 1 9 9 7 7 6 6 4 9 2 2 8 8 5 5 10
output
2 12 17 32
解题思路
还是很容易想到暴力做法的,就是考虑把中每个节点都变成根,然后求从根节点到其他点的最长路径,以及变到根节点所需要的交换次数,那么以为根的最大利益就是。然后枚举所有的情况取最大值就好了。
其中将节点变换到根节点所需要的交换次数就是初始树中从到的深度大小。
其实分析到这里就很容易想到换根dp,所以这题其实还蛮简单的。然后还有利用树的直径的性质来做的,这里先给出换根dp的做法。
对于以为根的树,定义表示从往下能走到的最远距离,表示从往下能走到的次远距离(非严格,即允许)。定义表示从的哪个子节点往下走能得到最远距离,换句话来说从往下走能得到。定义表示从的父节点往上走能走到的最远距离。
其中,和的求法很简单,只需要一次dfs就可以维护出这些信息,不再多说。
对于的求法,先画个图:
很明显如果,即从子节点往下走得不到最远距离,那么有。否则如果,那么从往上走然后再下走就又走到了,因此有。
最后如果要把变到根节点,那么从出发能到达的最远距离就是。然后枚举每一个,计算,并取最大值。
AC代码如下,时间复杂度为:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10, M = N * 2; 7 8 int head[N], e[M], ne[M], idx; 9 int dep[N], d1[N], d2[N], up[N], son[N]; 10 11 void add(int v, int w) { 12 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 13 } 14 15 void dfs_d(int u, int pre, int d) { 16 dep[u] = d; 17 d1[u] = d2[u] = 0; 18 for (int i = head[u]; i != -1; i = ne[i]) { 19 if (e[i] != pre) { 20 dfs_d(e[i], u, d + 1); 21 int t = d1[e[i]] + 1; 22 if (t >= d1[u]) d2[u] = d1[u], d1[u] = t, son[u] = e[i]; 23 else if (t > d2[u]) d2[u] = t; 24 } 25 } 26 } 27 28 void dfs_up(int u, int pre) { 29 for (int i = head[u]; i != -1; i = ne[i]) { 30 if (e[i] != pre) { 31 if (son[u] == e[i]) up[e[i]] = max(up[u], d2[u]) + 1; 32 else up[e[i]] = max(up[u], d1[u]) + 1; 33 dfs_up(e[i], u); 34 } 35 } 36 } 37 38 void solve() { 39 int n, k, c; 40 scanf("%d %d %d", &n, &k, &c); 41 memset(head, -1, sizeof(head)); 42 idx = 0; 43 for (int i = 0; i < n - 1; i++) { 44 int v, w; 45 scanf("%d %d", &v, &w); 46 add(v, w), add(w, v); 47 } 48 dfs_d(1, -1, 0); 49 dfs_up(1, -1); 50 LL ret = 0; 51 for (int i = 1; i <= n; i++) { 52 ret = max(ret, 1ll * k * max({d1[i], d2[i], up[i]}) - 1ll * c * dep[i]); 53 } 54 printf("%lld\n", ret); 55 } 56 57 int main() { 58 int t; 59 scanf("%d", &t); 60 while (t--) { 61 solve(); 62 } 63 64 return 0; 65 }
再给出另外一种做法。首先需要知道性质:已知树的直径的两个端点为和,那么树中任意一点所能走到的最远的点必然是和中的一个。证明可以参考链接。
因此可以先通过两次dfs找到树的直径的两个端点与,然后求树中所有点到与的距离,取两者中的最大值,就是该节点所能走到其他点的最大距离。最后枚举所有的,求并取最大值。
AC代码如下,时间复杂度为:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 6 const int N = 2e5 + 10, M = N * 2; 7 8 int head[N], e[M], ne[M], idx; 9 int dep[N], d1[N], d2[N]; 10 11 void add(int v, int w) { 12 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 13 } 14 15 void dfs(int u, int pre, int *d) { 16 for (int i = head[u]; i != -1; i = ne[i]) { 17 if (e[i] != pre) { 18 d[e[i]] = d[u] + 1; 19 dfs(e[i], u, d); 20 } 21 } 22 } 23 24 void solve() { 25 int n, k, c; 26 scanf("%d %d %d", &n, &k, &c); 27 memset(head, -1, sizeof(head)); 28 idx = 0; 29 for (int i = 0; i < n - 1; i++) { 30 int v, w; 31 scanf("%d %d", &v, &w); 32 add(v, w), add(w, v); 33 } 34 dep[1] = 0; 35 dfs(1, -1, dep); 36 int x = max_element(dep + 1, dep + n + 1) - dep; 37 d1[x] = 0; 38 dfs(x, -1, d1); 39 int y = max_element(d1, d1 + n + 1) - d1; 40 d2[y] = 0; 41 dfs(y, -1, d2); 42 LL ret = 0; 43 for (int i = 1; i <= n; i++) { 44 ret = max(ret, 1ll * max(d1[i], d2[i]) * k - 1ll * dep[i] * c); 45 } 46 printf("%lld\n", ret); 47 } 48 49 int main() { 50 int t; 51 scanf("%d", &t); 52 while (t--) { 53 solve(); 54 } 55 56 return 0; 57 }
参考资料
Codeforces Round #867 (Div. 3) Editorial:https://codeforces.com/blog/entry/115409
Codeforces Round 867 (Div. 3) A - G:https://zhuanlan.zhihu.com/p/624648254
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17356552.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效