2017 江苏省赛(湘潭市ccpc)H题

2017年湘潭市及江苏省赛H题解析,涉及树的边权优化问题。题目要求在原有树的基础上构建新边,使得总边权和最大化。通过分析,发现直径是关键,对于树上任意点,其最远端点为直径的端点之一。求解直径可通过两次DFS完成。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2017 湘潭市赛+江苏省赛H 题

Highway

题目链接:Highway
叉姐出的一个题目,超级有意思
题目意思是先给定一棵树,每条边有边权,然后要重新建n-1条边,新边的边权等于原来两点之间的距离,问说怎么建边使得总边权和最大,求最大的边权和
很巧妙的一个题目,最最朴素的做法就是处理出任意两点之间的距离,然后排序求一个最大生成树,但是复杂度太高了.
于是我们可以发现到对于原有的这棵树,它的直径肯定是要作为一条边加进去的,然后对于直径上的任意点,距离它最远的肯定是直径的两个端点之一,(反证一下就可以发现),我们用dis1[u]表示u点到直径一个端点的距离,dis2[u]表示u到另一个端点的距离.于是u要添加到新的树里时,贡献是max(dis1[u],dis2[u]) .
对于不在这个直径上的点,我们分析一下,也可以发现也是如此.于是这个问题的关键就是求树的直径了,两遍dfs即可(我比较鬼畜,做了4次).
通过这道题可以记住一个结论,在一棵树上,对于任一点,距离他最远的一定是直径的两个端点之一.

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<list>
#include<stack>
#include<queue>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG  MOD=1e9+ 7;
const double PI=acos(-1.0);
#define clrI(x) memset(x,-1,sizeof(x))
#define clr0(x) memset(x,0,sizeof x)
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
#define lson  l , mid , rt<< 1
#define rson  mid + 1 ,r , (rt<<1)+1
#define root 1, m , 1
const int MAXN = 1e5 + 1000 ;
struct Edge
{
    int u , to ;
    int next ;
    LONG w ;
}edge[MAXN*2 ] ;
int head[MAXN] ;
LONG dis[MAXN] ;
int tot = 0 ;
int p1 , p2 ;
LONG ans = 0 ;
LONG maxn = 0 ;
void Init()
{
    clrI(head) ;
    tot = 0 ;
    ans = 0 ;
}
void Add_edge(int u , int v , LONG w)
{
    edge[++tot].u = u ;
    edge[tot].to = v ;
    edge[tot].w = w ;
    edge[tot].next = head[u] ;
    head[u] = tot ;
}
void dfs1(int pre , int u )
{
    for(int i = head[u] ; i != -1 ; i =edge[i].next)
    {
        int v = edge[i].to ;
        if(pre == v)
        continue ;
        dis[v] = dis[u] + edge[i].w ;
        if(dis[v] > maxn )
        {
            maxn = dis[v] ;
            p1 = v ;
        }
        dfs1(u , v ) ;
    }
}
void dfs2(int pre , int u )
{
    for(int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].to ;
        if(v == pre)continue ;
        dis[v] = dis[u] + edge[i].w ;
        if(dis[v] >maxn)
        {
            maxn = dis[v] ;
            p2 = v ;
        }
        dfs2(u , v) ;
    }
}
LONG dis1 [MAXN] , dis2[MAXN] ;
void Get_dis1(int pre ,int u )
{
    for(int i = head[u] ; i != -1 ; i =edge[i].next)
    {
        int v=  edge[i].to ;
        if(pre == v) continue ;
        dis1[v] = dis1[u] + edge[i].w ;
        Get_dis1(u , v) ;
    }
}
void Get_dis2(int pre ,int u )
{
    for(int i = head[u] ; i != -1 ; i =edge[i].next)
    {
        int v =  edge[i].to ;
        if(pre == v) continue ;
        dis2[v] = dis2[u] + edge[i].w ;
        Get_dis2(u , v) ;
    }
}
int main()
{
    int n ;
    while(cin >> n)
    {
        Init() ;
        int u , v ;
        LONG w ;
        for(int i= 1 ; i< n ; ++i)
        {
            scanf("%d%d%I64d",&u ,& v , &w) ;
            Add_edge(u , v , w ) ;
            Add_edge(v , u , w ) ;
        }
        dis[1] = 0 ;
        maxn = 0 ;
        dfs1(-1 , 1) ;
        //
        maxn =0 ;
        dis[p1] = 0 ;
        dfs2(-1 , p1) ;
        ans += dis[p2] ;
        dis1[p1] = 0 ;
        Get_dis1(-1 , p1) ;
        dis2[p2] = 0 ;
        Get_dis2(-1 , p2) ;
        for(int i = 1 ; i <= n ;++i)
        {
            if(i == p1 || i == p2)continue ;
            ans += max(dis1[i] ,dis2[i]) ;
        }
        cout<<ans<<endl;
    }
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值