F. Gardening Friends

F. Gardening Friends

Two friends, Alisa and Yuki, planted a tree with n 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 k. Initially, vertex 1 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 u and v is the sum of the lengths of the edges on the path from u to v.

The girls took a course in gardening, so they know how to modify the tree. Alisa and Yuki can spend c 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 t (1t104) — the number of test cases.

The description of the test cases follows.

The first line of each test case contains integers n, k, c (2n2105; 1k,c109) — the number of vertices in the tree, the length of each edge, and the cost of the operation.

The next n1 lines of the test case contain pairs of integers ui, vi (1ui,vin) — the edges of the graph. These edges form a tree.

The sum of the values of n over all test cases does not exceed 2105.

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

 

解题思路

  还是很容易想到暴力做法的,就是考虑把1n中每个节点u都变成根,然后求从根节点u到其他点的最长路径d,以及u变到根节点所需要的交换次数dep,那么以u为根的最大利益就是dkdepc。然后枚举所有的情况取最大值就好了。

  其中将节点u变换到根节点所需要的交换次数就是初始树中从1u的深度大小。

  其实分析到这里就很容易想到换根dp,所以这题其实还蛮简单的。然后还有利用树的直径的性质来做的,这里先给出换根dp的做法。

  对于以1为根的树,定义d1[u]表示从u往下能走到的最远距离,d2[u]表示从u往下能走到的次远距离(非严格,即允许d1[u]=d2[u])。定义son[u]表示从u的哪个子节点往下走能得到最远距离,换句话来说从son[u]往下走能得到d1[u]。定义up[u]表示从u的父节点往上走能走到的最远距离。

  其中d1[u]d2[u]son[u]的求法很简单,只需要一次dfs就可以维护出这些信息,不再多说。

  对于up[u]的求法,先画个图:

  很明显如果son[fa]u,即fa从子节点u往下走得不到最远距离,那么有up[u]=max{d1[fa],up[fa]}+1。否则如果son[fa]=u,那么从u往上走然后再下走就又走到u了,因此有up[u]=max{d2[fa],up[fa]}+1

  最后如果要把u变到根节点,那么从u出发能到达的最远距离就是max{d1[i],d2[i],up[i]}。然后枚举每一个u,计算max{d1[u],d2[u],up[u]}kdep[u]c,并取最大值。

  AC代码如下,时间复杂度为O(n)

复制代码
 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 }
复制代码

  再给出另外一种做法。首先需要知道性质:已知树的直径的两个端点为xy,那么树中任意一点所能走到的最远的点必然是xy中的一个。证明可以参考链接

  因此可以先通过两次dfs找到树的直径的两个端点xy,然后求树中所有点到xy的距离,取两者中的最大值,就是该节点所能走到其他点的最大距离。最后枚举所有的u,求dmax[u]kdep[u]c并取最大值。

  AC代码如下,时间复杂度为O(n)

复制代码
 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

posted @   onlyblues  阅读(214)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示