【思维题】【树】【哈密顿路径】AGC018 D —— Tree and Hamilton Path

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34283998/article/details/82961774

题目传送门

想到了按边算贡献,然而却偏向了在直径上鬼畜的道路.

通过观察,可以发现如果多次经过重心,答案应该是最优的.因为这样使得除了某一条特殊的边之外,所有的边都会被计算两次.而这条特殊的边有两种情况,首先是只有一个重心,那么我们选择边权最小的且其中一个端点为重心的边;另一种情况是具有两个重心,选择两个重心之间的边即可.

此处复习一下求重心的方法,首先任选一个节点为根,统计出每一个节点对应子树的大小,最大儿子的大小.然后可以求出以这个点为根最大的子树大小,当一个节点的最大子树小于等于节点数的一半时,那么这个点就是重心.

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;
const int MAXN=1e5+5;
const ll INF=1e18;

int n,p1,p2;
ll ans,ex=INF;
int siz[MAXN],mx[MAXN];
vector<pair<int,int> > E[MAXN];

void dfs1(int u,int fa=0){
    siz[u]=1;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i].first;
        if(v==fa) continue;
        dfs1(v,u);
        siz[u]+=siz[v];
        mx[u]=max(mx[u],siz[v]);
    }
    mx[u]=max(mx[u],n-siz[u]);
    if(mx[u]<=n/2){
        if(!p1) p1=u;
        else p2=u;
    }
}

void dfs2(int u,int fa=0,ll sum=0){
    ans+=2*sum;
    for(int i=0;i<(int)E[u].size();i++){
        int v=E[u][i].first,val=E[u][i].second;
        if(v==fa) continue;
        dfs2(v,u,sum+val);
        if(v==p2||(!p2&&u==p1)) ex=min(ex,1ll*val);
    }
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v,val;
        scanf("%d%d%d",&u,&v,&val);
        E[u].push_back(make_pair(v,val));
        E[v].push_back(make_pair(u,val));
    }
    dfs1(1);
    dfs2(p1);
    printf("%lld",ans-ex);
}

猜你喜欢

转载自blog.csdn.net/qq_34283998/article/details/82961774