最近,@十一维的智子发送了一篇关于NOIP(没错,这货起死回生了)的文章。
相信小学生党们已经怒了…
没关系!今天我们就用一道CTSC的原题反击这个博主。
开始战斗!
[CTSC2017]网络
题目描述
一个一般的网络系统可以被描述成一张无向连通图。图上的每个节点为一个服务器,连接服务器与服务器的数据线则看作图上的一条边,边权为该数据线的长度。两个服务器之间的通讯距离定义为其对应节点之间最短路的长度。
现在,考虑一个当前图结构为树的网络系统。你作为该网络系统的管理员,被要求在这个系统中新加入一条给定长度的数据线。数据线可以连在任意两个服务器上。你的任务是,求出在所有合法的方案中,通讯距离最远的两个服务器之间的最小距离。
输入格式
输入包含多组数据。对于每组数据,输入的第一行包含二个正整数 N, L, 其中 N 表示服务器个数,L 为新加入数据线的长度。
接下来 n − 1 行,第 i 行有三个正整数 ai, bi, li,表示有一条长度为 li 的数据线连接服务器 ai, bi。服务器的编号为 1 ∼ N。
输入的末尾以两个 0 作为结束。
输出格式
对于每组数据,输出一行一个整数,描述在所有合法的方案中,通讯距离最远的两个服务器之间的最小距离。
输入输出样例
输入 #1复制
7 1
1 2 1
2 3 1
3 4 1
4 5 1
5 6 1
6 7 1
0 0
输出 #1
3
输入 #2
6 26
1 2 66
2 3 11
3 4 73
2 5 77
3 6 33
10 47
1 2 86
2 3 69
3 4 41
4 5 26
5 6 41
2 7 73
3 8 77
4 9 2
5 10 65
0 0
输出 #2
143
232
说明/提示
一共有 20 个测试点。编号为 1 ∼ 20。每个测试点为 5 分。
保证在任一测试点中,数据的组数不会超过 15,且所有数据的 N 之和不超过数据范围中标明的 N 的最大值的 5 倍。
保证所有的输入数据均为不超过 2^31 − 1 的非负整数,保证 N ≥ 1。
保证数据合法。
给定一棵带权树,你可以在某对点之间连一条长度为len的边,最小化任意两点间最短路的最大值。
肯定要二分。
有一个重要的结论:我们连边的端点一定在直径上。证明:
首先有一个很容易自己证明的性质:修改后最长的最短路一定和直径有交集。我们以一段不在直径上为例:
无标题.png
红色是直径,黑色是直径上挂着的子树,蓝色是最优解的边。那么我们将蓝色边的右端点上移到直径上的对应点(设为A),答案不会变差。因为所有从A子树里延伸过来的最长链一定不长于A点右侧的直径,所以虽然黑色子树里的点多走了一点,但不会改变答案。
然后我们继续思考:找出任何一条直径,然后我们限制加入新边后的距离\le mid≤mid,我们可以这样表示直径上两个点的距离(从左到右重新标号了),设sum为直径边权的前缀和,dis为子树里的最长链,设答案两点为a,b(a<b)a,b(a<b):min(dis[i]-sum[i]+dis[j]+sum[j],dis[i]+|dis[i]-dis[a]|+dis[j]+|dis[j]-dis[b]|)
min(dis[i]−sum[i]+dis[j]+sum[j],dis[i]+∣dis[i]−dis[a]∣+dis[j]+∣dis[j]−dis[b]∣)
使得上式\le mid≤mid,注意前一半和选择没有关系,我们可以用双指针,找出前一半不满足要求的pair,它们必须满足后一个要求。
观察到绝对值,我们有一个套路:枚举绝对值内部的符号,当我们枚举的情况和实际不符时,并不会改变答案,因为它的限制变得更弱了。我们把4种情况的式子写出来,发现是形如sum[a]\pm sum[b] \ge \tt{or}\le …sum[a]±sum[b]≥or≤…
我们求出\ge v≥v限制的v最大值,\le v≤v限制的v的最小值(可能需要使用树状数组,因为别忘了还有i<ji<j这个条件呢)
于是就变成求是否存在(a,b)(a,b),满足上述四个条件了,维护四个指针,判一下区间的交集即可。
#define N 100005
const LL inf=1e16;
// WARN long long !
int len;
vector< pii > E[N];
LL mxd[N]; int mxdk[N];
void dfs(int x,int _fa)
{
mxd[x]=mxdk[x]=0;
for(solid it:E[x])
{
int v=it.fi,w=it.se;
if(v==_fa) continue;
dfs(v