1 条题解
-
0
自动搬运
来自洛谷,原作者为

木xx木大
已经 AFO 的菜鸡 OIer搬运于
2025-08-24 21:52:32,当前版本为作者最后更新于2021-02-03 17:37:00,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
首先有一个结论:存在一组最优解,使得新加入的边所连接的两个节点 都在原树的直径上。
证明:(参考yhx巨佬的博客)
假设原树的直径为 ,长度为 。显然新添一条边后的直径 一定
若 在同一 的子树内,则 ,显然不优。所以 一定在不同 子树内。
若 ,设 表示以 为根的子树内 的父亲,考虑连 改为连 的答案变化。
若 两点同在环上或环外,环由 变为 ,环长变小,两点间距离不增
若 在环上, 在环外,若 的最短路径为 ,由直径的性质知它的长度不超过 的长度。
改为连接 后,同理有 。而 。所以从 调整为 答案不增。若一直这样调整,则 都在原树的直径上时最优。
对于直径上每个点 ,只有其子树内由 发出的最长链有可能影响答案。
然后问题就转化为了:一条链上每个点挂了一条边,现在可以添加一条长度为 的边连接链上的两点,使得图上两点间最大距离最小。
看到
最大距离最小想到二分,设二分距离为 ,接下来考虑如何 check 即可。为方便表述,设 表示到直径一端的距离, 表示 挂的边的长度。
假设 ,对于所有 的,必须满足
把绝对值拆开得到
$$NN=-L_i-L_j+d_i+d_j+c-D\le -L_a-L_b\\ PN=L_i-L_j+d_i+d_j+c-D\le L_a-L_b\\ NP=-L_i+L_j+d_i+d_j+c-D\le -L_a+L_b\\ PP=L_i+L_j+d_i+d_j+c-D\le L_a+L_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; }
- 1
信息
- ID
- 2737
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者