• [SDOI2013]直径(树的直径)


    [SDOI2013]直径

    题目描述

    小Q最近学习了一些图论知识。根据课本,有如下定义。树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度。如果一棵树有N个节点,可以证明其有且仅有N-1 条边。

    路径:一棵树上,任意两个节点之间最多有一条简单路径。我们用 dis(a,b)表示点a和点b的路径上各边长度之和。称dis(a,b)为a、b两个节点间的距离。

    直径:一棵树上,最长的路径为树的直径。树的直径可能不是唯一的。

    现在小Q想知道,对于给定的一棵树,其直径的长度是多少,以及有多少条边满足所有的直径都经过该边。

    输入输出格式

    输入格式:

    第一行包含一个整数N,表示节点数。 接下来N-1行,每行三个整数a, b, c ,表示点 a和点b之间有一条长度为c的无向边。

    输出格式:

    共两行。第一行一个整数,表示直径的长度。第二行一个整数,表示被所有直径经过的边的数量。

    输入输出样例

    输入样例#1: 复制

    6
    3 1 1000
    1 4 10
    4 2 100
    4 5 50
    4 6 100

    输出样例#1: 复制

    1110
    2

    说明

    【样例说明】 直径共有两条,3 到2的路径和3到6的路径。这两条直径都经过边(3, 1)和边(1, 4)。

    对于100%的测试数据:2<=N<=200000,所有点的编号都在1..N的范围内,边的权值<=10^9。



    题解


    这道题目比较玄学。
    我们知道对一颗树不止一条直径,那么被所有直径经过的边,
    随着直径的分叉而不断收缩。
    而直径的分叉点一定是在直径上的。
    但是我并不知道为什么要从起点推一遍收缩的一段,再从树的直径的末尾推一遍收缩的另一端。这两段之间的路径就是解。
    但是一遍循环是推不出的,必须循环两次??
    如果有巨佬一个循环搞出了两个段点可以告诉本蒟蒻qwq。




    代码


    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const int N=200001;
    ll dis[N],maxx,s,t;
    ll n,m,num,head[N],vis[N];
    struct node{
        int to,nex;
        ll v;
    }e[N<<1];
    ll dep[N],ff[N],l,r,ans,son[N];
    void add(int from,int to,ll v){
        num++;
        e[num].to=to;
        e[num].v=v;
        e[num].nex=head[from];
        head[from]=num;
    }
    
    ll read(){
        ll x=0,w=1;char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*w;
    }
    
    void dfs(int x,int fa){
        for(int i=head[x];i;i=e[i].nex){
            int v=e[i].to;if(v==fa)continue;ff[v]=x;
            dis[v]=dis[x]+e[i].v;dfs(v,x);
        }
    }
    
    void dfs2(int u,int fa){
        dep[u]=0;ll maxn=0;
        for(int i=head[u];i;i=e[i].nex){
            int v=e[i].to;if(v==ff[u]||vis[v]==1)continue;
            dfs2(v,u);maxn=max(maxn,dep[v]+e[i].v);
        }
        dep[u]=maxn;	
    }
    
    int main(){
        n=read();
        for(int i=1;i<n;i++)
        {
            ll x=read(),y=read(),z=read();
            add(x,y,z);add(y,x,z);
        }
        dfs(1,0);
        for(int i=1;i<=n;i++){if(dis[i]>maxx)maxx=dis[i],s=i;dis[i]=0;}
        dfs(s,0);maxx=0;
        for(int i=1;i<=n;i++)if(dis[i]>maxx)maxx=dis[i],t=i;
        printf("%lld
    ",maxx);
        l=t;r=s;
        int now=t;
        while(now!=s){
            vis[now]=1;son[ff[now]]=now;now=ff[now];
        }
        now=t;
        while(now!=s){
            dep[now]=0;
            dfs2(now,0);
            if(dep[now]==maxx-dis[now])l=now;
            now=ff[now];
        }
        now=s;
        while(now){
            dfs2(now,0);
            if(dep[now]==dis[now])r=now;
            now=son[now];
        }
        while(l!=r&&l){
            l=ff[l];ans++;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    如何基于 String 实现同步锁?
    Web前端开发必不可少的9个开源框架
    Java知识,面试总会问到虚拟机,虚拟机类加载机制你懂吗?
    带你了解Java的序列化与反序列化
    想自己写框架?不了解Java注解机制可不行
    深度解密:Java与线程的关系
    手把手教你分析Mysql死锁问题
    windows server 2012 安装 DockerToolbox
    .NET CORE MVC  返回 JSON 数据
    .net core ajax提交Controller接收不到的问题处理方法
  • 原文地址:https://www.cnblogs.com/hhh1109/p/9525018.html
Copyright © 2020-2023  润新知