ABC368G

· · 题解

思路

首先思考 Takahashi 如何让自己走过的路径最小。根据题意,一定要从 1 号点出发并回到 1 号点,所以每一个点到 1 号点之间的边一定是要经过两次的。

知道了计算经过所有点的路径长度的方法之后,就可以根据其特点发现,当选取部分点的时候,选子节点一定比选择他的祖先更优。因为选取子节点后,同样会经过其祖先,所以说选择一个点就相当于选择了其祖先。现在我们只需要统计所有的叶子的权值(到达此处的路径长度和)。此时我们会发现有多个儿子的点会导致路径长度的重复计算,我们需要将有多个儿子的节点的权值转移给它其中的一个儿子。因为选取点的时候是按照路径最长的方式选取,所以每一次都是要选取能使路径增加最长的点,所以我们选择将每个点的权值传递给他的重儿子。之后统计所有的叶子的权值,输出即可。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5;
struct nd{int y,z;};
int s[N],dp[N],n,ans;
priority_queue<int> q;
vector<nd> v[N];
void dfs(int x,int fa){//找重儿子
    int maxx=0;
    for(auto i:v[x]){
        if(i.y==fa) continue;
        dp[i.y]=i.z;
        dfs(i.y,x);
        if(dp[i.y]>maxx)maxx=dp[i.y],s[x]=i.y;
    }
    dp[x]+=maxx;
}
void dfss(int x,int fa){//计算所有点的权值
    for(auto i:v[x]){
        if(i.y==fa) continue;
        if(s[x]==i.y)dp[i.y]=dp[x]+i.z;
        else dp[i.y]=i.z;
        //转移给重儿子
        dfss(i.y,x);
    }if(v[x].size()==1&&fa!=0)q.push(dp[x]);
    //存下叶子
}
signed main() {
    cin>>n;
    for(int i=1,x,y,z;i<n;i++) {
        cin>>x>>y>>z;
        v[x].push_back({y,z});
        v[y].push_back({x,z});
    }
    dfs(1,0);dp[1]=0;dfss(1,0);
    //dp数组用了两次
    //在dfs中用于统计重儿子
    //在dfss中用于存储当前点的权值
    for(int i=1;i<=n;i++){
        if(q.size()) ans+=q.top()*2,q.pop();
        //在所有为选取的叶子中选择最大的
        //选完所有的叶子后,其他的点不在会对答案产生贡献
        cout<<ans<<"\n";
    }
    return 0;
}