大臣的旅费


大臣的旅费

很久以前,$T$ 王国空前繁荣。

为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。

为节省经费,$T$ 国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。

同时,如果不重复经过大城市,从首都到达每个大城市的方案都是唯一的。

J是T国重要大臣,他巡查于各大城市之间,体察民情。

所以,从一个城市马不停蹄地到另一个城市成了 $J$ 最常做的事情。

他有一个钱袋,用于存放往来城市间的路费。

聪明的J发现,如果不在某个城市停下来修整,在连续行进过程中,他所花的路费与他已走过的距离有关,在走第 $x$ 千米到第 $x+1$ 千米这一千米中($x$是整数),他花费的路费是 $x+10$ 这么多。也就是说走 $1$ 千米花费 $11$,走 $2$ 千米要花费 $23$。

$J$ 大臣想知道:他从某一个城市出发,中间不休息,到达另一个城市,所有可能花费的路费中最多是多少呢?

输入格式

输入的第一行包含一个整数 $n$,表示包括首都在内的T王国的城市数。

城市从 $1$ 开始依次编号,$1$ 号城市为首都。

接下来 $n?1$ 行,描述 $T$ 国的高速路($T$国的高速路一定是 $n?1$ 条)。

每行三个整数 $P_{i},Q_{i},D_{i}$,表示城市 $P_{i}$ 和城市 $Q_{i}$ 之间有一条双向高速路,长度为 $D_{i}$ 千米。

输出格式

输出一个整数,表示大臣 $J$ 最多花费的路费是多少。

数据范围

$1 \leq n \leq 10^{5}$,
$1 \leq P_{i},Q_{i} \leq n$,
$1 \leq D_{i} \leq 1000$

输入样例:

5 
1  2  2 
1  3  1 
2  4  5 
2  5  4 

输出样例:

135

解题思路

  根据题意,这是一个无环连通图,也就是一棵树。

  题目是问我们树上的最长路径是多少,也就是问我们树的直径。

  树的直径:一棵树上长度最长的路径。

  求树的直径的其中一种方法是用dfs:

  1. 任取一点$x$,求剩余的点到$x$的距离,并在这些点中找到一个到$x$距离最大的点$y$。
  2. 求所有点到$y$的距离,到$y$最大的距离就是树的直径。

  下面证明这种做法是正确的。

  我们从第一步找到一个距离$x$最远的点$y$,如果$y$是其中一个直径(可能存在多个大小相同的直径)的端点,那么根据第二步找到的离$y$最远的距离就是其中一条直径。

  用反证法,假设$y$不是任何一条直径的端点。

  情况$1$,假设存在一条直径,与$x$到$y$的路径有交点:

  假设$u$,$v$是直径的两个端点,与$x$到$y$的路径上有交点$u$。

  因为$y$是距离$x$最远的一个点之一,因此有$d_{xuy} \geq d_{xuv}$,有公共的一段$d_{xu}$,因此有$d_{uy} \geq d_{uv}$。加上同一段$d_{uw}$,因此有$d_{yuw} \geq d_{vuw}$。又因为$d_{vw}$是一条直径,因此$d_{yw}$也是一条直径,即$y$是直径的端点,与假设矛盾。

  情况$2$,假设存在一条直径,与$x$到$y$的路径没有交点:

  假设$u$,$v$是直径的两个端点,由于是一颗树(连通),因此会有一条路径$ab$连接$xy$和$vw$这两条路径。

  因为$y$是距离$x$最远的一个点之一,因此有$d_{xay} \geq d_{xabv}$,有公共的一段$d_{xa}$,因此有$d_{ay} \geq d_{abv}$。由于每一条路径的长度大于$0$,$d_{ab} > 0$,因此有$d_{ay} > d_{bv}$,$d_{yab} > d_{bv}$。加上同一段$d_{bw}$,因此有$d_{yabw} > d_{vw}$。又因为$d_{vw}$是一条直径,而$d_{yabw} > d_{vw}$,矛盾。

  因此正确性得到证明,$y$必然是某条直径的端点。

  AC代码如下:

 1 #include 
 2 #include 
 3 #include 
 4 using namespace std;
 5 
 6 const int N = 1e5 + 10, M = N << 1;
 7 
 8 int head[N], e[M], wts[M], ne[M], idx;
 9 int dist[N];
10 
11 void add(int v, int w, int wt) {
12     e[idx] = w, wts[idx] = wt, ne[idx] = head[v], head[v] = idx++;
13 }
14 
15 void dfs(int src, int pre, int d) {
16     dist[src] = d;
17     for (int i = head[src]; i != -1; i = ne[i]) {
18         if (e[i] != pre) {
19             dfs(e[i], src, d + wts[i]);
20         }
21     }
22 }
23 
24 int main() {
25     memset(head, -1, sizeof(head));
26     
27     int n;
28     scanf("%d", &n);
29     for (int i = 0; i < n - 1; i++) {
30         int v, w, wt;
31         scanf("%d %d %d", &v, &w, &wt);
32         add(v, w, wt), add(w, v, wt);
33     }
34     
35     dfs(1, 0, 0);   // 任取一点1,求其余点到1的距离
36     
37     // 找到距离1最远的点maxv
38     int maxv = 1;
39     for (int i = 1; i <= n; i++) {
40         if (dist[i] > dist[maxv]) maxv = i;
41     }
42     
43     dfs(maxv, 0, 0);    // 求所有点到maxv的距离
44     
45     // 找到离maxv最远的距离,这个距离就是树的直径
46     int maxd = -1;
47     for (int i = 1; i <= n; i++) {
48         maxd = max(maxd, dist[i]);
49     }
50     printf("%lld", 10 * maxd + maxd * (maxd + 1ll) / 2);
51     
52     return 0;
53 }

参考资料

  AcWing 1207. 大臣的旅费(蓝桥杯C++ AB组辅导课):https://www.acwing.com/video/710/