P3771 [CTSC2017]网络(树的直径+二分+双指针)
P3771 [CTSC2017]网络
首先有一个结论:存在一组最优解,使得新加入的边所连接的两个节点
证明:(参考yhx巨佬的博客)
假设原树的直径为
[s,u_1,u_2\dots t] ,长度为l 。显然新添一条边后的直径l' 一定\le l 若
a,b 在同一u_i 的子树内,则l'=l ,显然不优。所以a,b 一定在不同u_i 子树内。若
a\neq u_i ,设p_a 表示以u_i 为根的子树内a 的父亲,考虑连(a,b) 改为连(p_a,b) 的答案变化。若
x,y 两点同在环上或环外,环由[a\dots u_i\dots u_j\dots b,a] 变为[p_a\dots u_i\dots u_j\dots b,p_a] ,环长变小,两点间距离不增若
x 在环上,y 在环外,若x\to y 的最短路径为x\to a\to b\to y ,由直径的性质知它的长度不超过s\to a\to b\to t 的长度。改为连接
(p_a,b) 后,同理有len(x\to p_a\to b\to y)\le len(s\to p_a\to b\to t) 。而len(s\to p_a\to b\to t)\le len(s\to a\to b\to t) 。所以从(a,b) 调整为(p_a,b) 答案不增。若一直这样调整,则a,b 都在原树的直径上时最优。
对于直径上每个点
然后问题就转化为了:一条链上每个点挂了一条边,现在可以添加一条长度为
看到最大距离最小
想到二分,设二分距离为
为方便表述,设
假设
把绝对值拆开得到
则我们需要分别求出
求最大值:上式的左半部分可以看作
判断
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
namespace FGF
{
int n,m,V;
const int N=1e5+5;
const ll INF=0x3f3f3f3f3f3f3f3f;
struct edg{
int to,nxt,w;
}e[N<<1];
int cnt,head[N],pre[N],ro,is[N],q[N];
ll dis[N],L[N],d[N];
namespace shortcut
{
bool check(ll x)
{
ll PN,PP,NN,NP,PX,NX,cur;
int h=1,t=0;
PN=PP=NN=NP=PX=NX=-INF;
for(int i=1;i<=V;i++)
{
while(h<=t&&L[i]+d[i]-L[q[h]]+d[q[h]]>x)PX=max(PX,L[q[h]]+d[q[h]]),NX=max(NX,d[q[h]]-L[q[h]]),h++;
cur=d[i]+m-x;
PP=max(PP,L[i]+PX+cur),PN=max(PN,cur-L[i]+PX);
NN=max(NN,cur-L[i]+NX),NP=max(NP,cur+L[i]+NX);
while(h<=t&&d[q[t]]-L[q[t]]<d[i]-L[i])t--;
q[++t]=i;
}
int pos1=1,pos2=V+1,pos3=0,pos4=V;
for(int i=1;i<=V;i++)//这一段一定要想清楚再写,不然就会像我一样五行代码写两个小时
{
while(pos1<=V&&L[pos1]-L[i]<PN)pos1++;//[pos1,V]
while(pos2&&L[pos2-1]+L[i]>=PP)pos2--;//[pos2,V]
while(pos3<=V&&-L[pos3+1]+L[i]>=NP)pos3++;//[1,pos3]
while(pos4&&-L[pos4]-L[i]<NN)pos4--;//[1,pos4]
if(max(pos2,pos1)<=min(pos3,pos4))return 1;
}
return 0;
}
ll work()
{
ll l=0,r=INF,mid,ans;
while(l<=r)
{
mid=(l+r)>>1;
check(mid)?ans=mid,r=mid-1:l=mid+1;
}
return ans;
}
}
void add(int u,int v,int w)
{
cnt++;
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[cnt].w=w;
}
void dfs(int u,int f)
{
if(dis[ro]<dis[u])ro=u;
for(int i=head[u];i;i=e[i].nxt)
if(e[i].to!=f&&!is[e[i].to])pre[e[i].to]=i,dis[e[i].to]=dis[u]+e[i].w,dfs(e[i].to,u);
}
void work()
{
while(scanf("%d%d",&n,&m)&&n&&m)
{
memset(head,0,sizeof(head));cnt=1;
memset(is,0,sizeof(is));
for(int i=1,u,v,w;i<n;i++)
scanf("%d%d%d",&u,&v,&w),add(u,v,w),add(v,u,w);
ro=1,dis[ro]=pre[ro]=0;
dfs(ro,0);
int rt2;ll mx;
dis[ro]=pre[ro]=0;
dfs(ro,0);
rt2=ro,V=0,mx=dis[rt2];
for(int u=rt2;u;u=e[pre[u]^1].to)is[u]=1;
for(int u=rt2;u;u=e[pre[u]^1].to)
{
L[++V]=mx-dis[u];
ro=u,dis[u]=0,dfs(ro,0);
d[V]=dis[ro];
}
printf("%lld\n",shortcut::work());
}
}
}
int main()
{
FGF::work();
return 0;
}