最近,@十一维的智子发送了一篇关于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,x);
if(ckmax(mxd[x],mxd[v]+w)) mxdk[x]=v;
}
}
template<LL f[]>il bool cmp(const int a,const int b) {return f[a]<f[b];}
LL sum[N],f[N],g[N]; int cnt;
int idf[N],idg[N];
void prework()
{
dfs(1,0);
int x=1; while(mxdk[x]) x=mxdk[x];
dfs(x,0);
while(x)
{
int nx=0; LL dis=0;
++cnt;
for(solid it:E[x])
{
int v=it.fi,w=it.se;
if(mxdk[v]==x) continue;
if(mxdk[x]==v) nx=v,sum[cnt+1]=sum[cnt]+w;
else ckmax(dis,mxd[v]+w);
}
f[cnt]=dis+sum[cnt],g[cnt]=dis-sum[cnt];
x=nx;
}
for(ri i=1; i<=cnt; ++i) idf[i]=idg[i]=i;
sort(idf+1,idf+1+cnt,cmp<f>),sort(idg+1,idg+1+cnt,cmp<g>);
}
struct BIT
{
LL tre[N];
il void upd(int x,const LL &v)
{
for(; x<=cnt; x+=x&-x) ckmax(tre[x],v);
}
il LL ask(int x)
{
LL res=-inf;
for(; x; x-=x&-x) ckmax(res,tre[x]);
return res;
}
} bf,bg;
il void clear2() {mem(bf.tre,0xcf),mem(bg.tre,0xcf);}
bool check(const LL &mid)
{
clear2();
int p=cnt+1;
LL l00=-inf,l01=-inf,l10=inf,l11=inf;
for(ri i=1; i<=cnt; ++i)
{
while(p>1&&g[idg[p-1]]+f[idf[i]]>mid)
{
--p;
bf.upd(idg[p],f[idg[p]]);
bg.upd(idg[p],g[idg[p]]);
}
LL t=bf.ask(idf[i]-1);
ckmax(l00,t+f[idf[i]]);
ckmax(l01,t+g[idf[i]]);
t=bg.ask(idf[i]-1);
ckmin(l10,-t-f[idf[i]]);
ckmin(l11,-t-g[idf[i]]);
}
l00+=len-mid,l01+=len-mid;
l10+=mid-len,l11+=mid-len;
if(l00>l11||l01>l10) return 0;
p=cnt+1; int q=cnt,r=0,s=1; // [p,q], [s,r]
for(ri i=1; i<=cnt; ++i)
{
while(p>1&&sum[i]+sum[p-1]>=l00) --p;
while(q>=1&&sum[i]+sum[q]>l11) --q;
while(r<cnt&&sum[i]-sum[r+1]>=l01) ++r;
while(s<=cnt&&sum[i]-sum[s]>l10) ++s;
if(p<=q&&s<=r&&!(q<s||r<p)) return 1;
}
return 0;
}
void solve()
{
LL l=1,r=inf,ans=-1;
while(l<=r)
{
LL mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
out(ans);
}
int n;
void clear()
{
cnt=0;
for(ri i=1; i<=n; ++i) E[i].clear();
}
signed main()
{
#ifdef M207
freopen("in.in","r",stdin);
// freopen("ot.out","w",stdout);
#endif
while(in(n,len),n)
{
clear();
for(ri i=1,a,b,c; i<n; ++i)
{
in(a,b,c);
E[a].pb(mp(b,c)),E[b].pb(mp(a,c));
}
prework();
solve();
}
return 0;
}
来源:CSDN
作者:代码酱
链接:https://blog.csdn.net/Coduck/article/details/104066279