1. 程式人生 > >[NOI2003]逃學的小孩 --- 樹的直徑

[NOI2003]逃學的小孩 --- 樹的直徑

傳送門:洛谷P4408


題目描述

Chris家的電話鈴響起了,裡面傳出了Chris的老師焦急的聲音:“喂,是Chris的家長嗎?你們的孩子又沒來上課,不想參加考試了嗎?”一聽說要考試,Chris的父母就心急如焚,他們決定在儘量短的時間內找到Chris。他們告訴Chris的老師:“根據以往的經驗,Chris現在必然躲在朋友Shermie或Yashiro家裡偷玩《拳皇》遊戲。現在,我們就從家出發去找Chris,一但找到,我們立刻給您打電話。”說完砰的一聲把電話掛了。

Chris居住的城市由N個居住點和若干條連線居住點的雙向街道組成,經過街道x需花費Tx分鐘。可以保證,任兩個居住點間有且僅有一條通路。Chris家在點C

Shermie和Yashiro分別住在點A和點B。Chris的老師和Chris的父母都有城市地圖,但Chris的父母知道點A、B、C的具體位置而Chris的老師不知。

為了儘快找到Chris,Chris的父母會遵守以下兩條規則:

  1. 如果A距離C比B距離C近,那麼Chris的父母先去Shermie家尋找Chris,如果找不到,Chris的父母再去Yashiro家;反之亦然。
  2. Chris的父母總沿著兩點間唯一的通路行走。

顯然,Chris的老師知道Chris的父母在尋找Chris的過程中會遵守以上兩條規則,但由於他並不知道A,B,C的具體位置,所以現在他希望你告訴他,最壞情況下Chris的父母要耗費多長時間才能找到Chris?


分析

樹的直徑題都這麼毒瘤麼,題面不可讀系列
可以將題目的要求拆成兩部分 求 A B / C A\to B / C 最小值的最大值 和 B

C B \to C 的最大值
求樹上最長路徑的的話,那就是樹的直徑了, 因此 B C B \to C 必定為樹的直徑.兩遍dfs確定好 B B C C 點即可,
對於 A B / C A\to B / C ,可以將其拆成兩部分, A P A \to P (P在樹的直徑上),以及 m i n ( P B , P C ) min(P \to B, P \to C) ,對此,列舉直徑上的每個點,求出不經過直徑其他點最長距離(即為 A P A \to P ).比較更新答案即可.


程式碼

#include <cstdio>
#include <cstdlib>
#include <cstring>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int to[400005], nxt[400005], val[400005];
int last[200005];
int cnt;
IL void add(int u, int v, int w)
{
    to[++cnt] = v; nxt[cnt] = last[u]; val[cnt] = w; last[u] = cnt;
    to[++cnt] = u; nxt[cnt] = last[v]; val[cnt] = w; last[v] = cnt;
}

typedef long long ll;

int n, m;
ll dis[200005];
int fa[200005];

bool check[200005];
IL ll min_(ll x, ll y) { return x < y ? x : y; }
IL ll max_(ll x, ll y) { return x > y ? x : y; }

IL void dfs(int u, int &p)
{
    if(dis[u] > dis[p]) p = u;
    for(int i = last[u], v; (v = to[i]); i = nxt[i])
    if(v != fa[u])
    {
        dis[v] = dis[u] + val[i];
        fa[v] = u;
        dfs(v, p);
    }
}

IL void dfs2(int u, int pre, ll d, ll &maxd)
{
    if(d > maxd) maxd = d;
    for(int i = last[u], v; (v = to[i]); i = nxt[i])
    if(v != pre && !check[v])
        dfs2(v, u, d + val[i], maxd);
}

int main()
{
    n = read(); m = read();
    
    for(int i = 1, x, y; i <= m; ++i)
    {
    	x = read(); y = read();
    	add(x, y, read());
    }
    
    int S = 0, T = 0;
    dfs(1, T);
    dis[T] = 0; fa[T] = 0;
    dfs(T, S);
    
    for(int i = S; i; i = fa[i])
    	check[i] = 1;
    ll ans = 0, maxd;
    for(int i = S; i; i = fa[i])
    {
    	maxd = 0;
    	dfs2(i, 0, min_(dis[i], dis[S] - dis[i]), maxd);
    	ans = max_(ans, maxd);
    }
    ans += dis[S];
    printf("%lld\n", ans);
    
    return 0;
}