1. 程式人生 > >[SDOI2013]直徑(樹的直徑)

[SDOI2013]直徑(樹的直徑)

str 學習 sin clas long long ont tro 給定 pac

[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\n",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\n",ans);
    return 0;
}

[SDOI2013]直徑(樹的直徑)