欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

树上最长路 sol

程序员文章站 2022-05-12 12:26:01
...

树上最长路


题目描述

给定一棵有nn个结点的树,树上每条边的长度为wiw_i.

定义一棵树上的最长路为所有点对间,最短路最长的那对点间的最短路径长度。

在树中删去任意一条边,都会使得原树分为恰好两棵互相之间不连通的子树,现在AA君想知道删去每一条边后, 剩下的两棵子树中的最长路的较大值是多少。

为了方便起见,你只需要告诉 AA 君删去每一条边后得到的最长路较大值的和。

输入格式

第一行一个整数nn,表示树的结点数

接下来n1n-1行每行三个整数u,v,wu,v,w

表示一条连接u,vu,v的长度为ww的无向边

输出格式

仅一行一个整数表示答案

样例

input

5
2 1 7
3 1 7
4 2 5
5 2 6

output

63

数据范围

20%20\%的数据:n20n\leq 20

50%50\%的数据:n2000n\leq 2000

另外20%20\%的数据:所有点度数均小于44且最多只有一个点度数为33

另有20%20\%的数据:树为随机生成,第ii条边连接[1,i][1,i]间随机一点与点i+1i+1
100%100\%的数据: 1n1051\leq n\leq 10^51w10001\leq w\leq 1000

sol:

对于前50%50\%的数据,都可以枚举删除每条边,直接做

下面考虑正解:

首先对于一个不在直径上的边,删除它之后对答案的贡献不变

树上最长路 sol

对于此图,若我们删除363-6这条边,则这次删除操作对于答案的没有影响,还是直径

若对于直径上的边,则答案就是以被删除的边的两个点为根分别求出两个联通快的最长链和次长链

然后就没了。。

具体细节看代码注释

Code:

#include<bits/stdc++.h>
using namespace std;
#define f1(a,b,c) for(ll c=a;c<=b;c++)
#define f2(a,b,c) for(int c=a;c>=b;c--)
#define f3(a,b,c) for(int c=a;c;c=b)
#define so1(a,n) sort(a+1,a+n+1,mycmp);
#define so2(a,n) sort(a+1,a+n+1);
#define re(a,n) reverse(a+1,a+n+1);
#define ll long long
#define itn int 
#define ubt int 
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define DEBUG puts("FUCK");
const int twx=1e5+100;
const int inf=0x3f3f3f3f;
ll read()
{
    ll sum=0;
    ll flag=1;
    char c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')
        {
            flag=-1;
        }
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=((sum*10)+c-'0');
        c=getchar();
    }
    return sum*flag;
}
vector<pll> G[twx];
ll n;
ll d[twx];
ll X[twx];
ll Y[twx];
ll on[twx];
ll s;
ll t;
ll MAX[twx][3][2];
ll Deep[twx][2];
ll ans=0;
void get_diameter(ll x,ll fa)//求直径
{
    f1(0,(ll)G[x].size()-1,i)
    {
        ll y=G[x][i].first;
        ll z=G[x][i].second;
        if(y==fa)
        {
            continue ;
        }
        d[y]=d[x]+z;
        get_diameter(y,x);
    }
}
bool get_on_diameter(ll x,ll fa)//判断哪些点在直径上
{
    if(x==t)
    {
        on[x]=1;
        return 1;
    }
    f1(0,(int)G[x].size()-1,i)
    {
        ll y=G[x][i].first;
        if(y==fa)
        {
            continue ;
        }
        on[x]|=get_on_diameter(y,x);
    }
    return on[x];
}
void get_endpoint()//求直径的两个端点
{
    get_diameter(1,0);
    f1(1,n,i)
    {
        if(d[i]>d[s])
        {
            s=i;
        }
    }
    d[s]=0;
    get_diameter(s,0);
    f1(1,n,i)
    {
        if(d[i]>d[t])
        {
            t=i;
        }
    }
    on[s]=1;
}
void get_second_path(ll op,ll x,ll fa)
{
    Deep[x][op]=Deep[fa][op]+1;
    f1(0,(ll)G[x].size()-1,i)
    {
        ll y=G[x][i].first;
        ll z=G[x][i].second;
        if(y==fa)
        {
            continue ;
        }
        get_second_path(op,y,x);
        ll tmp=MAX[y][1][op]+z;
        if(tmp>MAX[x][1][op])
        {
            MAX[x][2][op]=MAX[x][1][op];
            MAX[x][1][op]=tmp;
        }
        else if(tmp>MAX[x][2][op])
        {
            MAX[x][2][op]=tmp;
        }
        MAX[x][0][op]=max(MAX[x][0][op],MAX[y][0][op]);
    }
    MAX[x][0][op]=max(MAX[x][0][op],MAX[x][1][op]+MAX[x][2][op]);
}
void init()
{
    n=read();
    f1(1,n-1,i)
    {
        ll x=read();
        ll y=read();
        ll z=read();
        G[x].pb(mp(y,z));
        G[y].pb(mp(x,z));
        X[i]=x;
        Y[i]=y;
    }
}
void work()
{
    get_on_diameter(s,0);
    get_second_path(0,s,0);
    get_second_path(1,t,0);
    f1(1,n-1,i)
    {
        ll XX=X[i];
        ll YY=Y[i];
        if(!on[XX]||!on[YY])//不在直径上的边贡献直接加
        {
            ans+=d[t];
            continue ;
        }
        if(Deep[XX][0]<Deep[YY][0])
        {
            ans+=max(MAX[XX][0][1],MAX[YY][0][0]);
        }
        else
        {
            ans+=max(MAX[XX][0][0],MAX[YY][0][1]);
        }
    }
}
void print()
{
    printf("%lld\n",ans);
}
int main()
{
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);
    init();
    get_endpoint();
    work();
    print();
	return 0;
}