F. Gardening Friends

题目链接:Problem - F - Codeforces

题目大意:给一颗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;
}

感谢你的收看与点赞, 欢迎大佬的指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值