题目大意:给一颗n结点的树, 起初根结点为1, 树的成本定义为树上所有顶点中从根到顶点的最大距离, 现在你可以有一种操作, 将根结点转移到相邻的结点,但会有操作成本成本的消耗。现将求最大利润(因为他要卖)。 利润 == 树的成本 - 操作的总成本。
输入的第一行包含一个整数 t( 1≤ t ≤1e4) - 测试用例数。
测试用例说明如下。
每个测试用例的第一行都包含一个整数 n 、 k 、 c(2 ≤ n ≤ 2⋅1e5 ; 1≤ k, c≤ 1e9 )--树中顶点的数量、每条边的长度以及操作的成本。
测试用例接下来的 n−1 行包含成对的整数 ui 、 vi ( 1 ≤ ui , vi ≤ n ) - 图的边。这些边构成了一棵树。
所有测试用例中 n 的值之和不超过 2 ⋅ 1e5 。
解题思路:相当于求每个结点当它作为根节点时的利润, 然后求一个最大值, 最后的答案要枚举每个结点作为根节点。 开始时: 求解以该节点为根节点的最大的距离, 因为告诉了边的边长,所以相当于求它的直径。
做法: 要做三次dfs, 感觉有点像 树的直径
第一次: 将移动根的花费成本计算出来, 然后将最远的点作为第二次的dfs的根结点。 第二次:再次做一次dfs, 然后又将新的距离保留下来, 然后将最远的点作为第三次的dfs的根结点。第三次:再将距离保留下来。
在最后枚举1---n的所有结点作为根节点, 计算答案 答案的计算 max(a[i], b[i]) *k - c*dis[i]. 其中max(a[i], b[i])求得最大的距离。
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
//传参数组, 方便做三次dfs
void dfs(vector<int>& ab, vector<vector<int>> &g, int u, int fa){
if(fa!=-1 && g[u].size()==1) {
ab[u] = ab[fa]+1;
return;
}
if(fa!=-1)
ab[u] = ab[fa]+1;
for(int v : g[u]) {
if(v==fa)continue;
dfs(ab, g, v,u);
}
}
void solve(){
int n, k, c;
cin >> n >> k >> c;
vector<vector<int>> g(n+1,vector<int>(0));
for(int i=1; i<n; i++) {
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> dis(n+1,0);
vector<int> a(n+1,0), b(n+1,0);
i64 ans = 0;
dfs(dis, g, 1, -1); // 第一次,求花费成本
int root = 1, ma = 0;
for(int i=1; i<=n; i++) { //找最远
if(dis[i] > ma) {
ma = dis[i];
root = i;
}
}
dfs(a, g, root, -1); //第二次
ma = 0;
for(int i=1; i<=n; i++) {
if(a[i] > ma) {
ma = a[i];
root = i;
}
}// 找最远
dfs(b, g, root, -1); //第三次
for(int i=1; i<=n; i++) {//枚举出答案
ans = max(1LL*max(a[i], b[i]) * k - 1LL*dis[i]*c, ans);
}
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int t;
cin >> t;
while(t--) {
solve();
}
return 0;
}
感谢你的收看与点赞, 欢迎大佬的指正。