### 原题链接 [https://oiclass.com/d/tigao/p/1468](https://oiclass.com/d/tigao/p/1468) ### 简略做法 对于查询,首先我们发现,如下图,红色部分一定去u,绿色部分一定去v,蓝色部分去哪里无所谓。
因此,重点在于找到这个中间点。 虽然这棵树是无根树,但我们可以指定一个根。 首先,要求u的深度不能比v浅。这个做个`swap(u,v)`即可。设`f=lca(u,v)`。 然后,用倍增求出某个分割点(称为sp),使得sp在u、v路径上,且$dis(sp,v)-2\le dis(sp,u) 如果你已经对sp感到云里雾里了,请看下文“思维碎片:为什么sp是这么设置的?”。 容易发现,sp就是u、f路径上最浅的点,使得 $dis(sp,u)classu[MAXN+5],classv[MAXN+5];//把所有查询根据u的值、根据v的值分类。需要注意u、v有序。 void dfs(int u) { for(int qid:classu[u])//对于所有u点为本点的查询: { ans[qid]=max(ans[qid],query(1,dfnl[que[qid].sp],dfnr[que[qid].sp]));//sp的子树的所有点到本点的最大距离? } for(int qid:classv[u])//对于所有v点为本点的查询: { if(dfnl[que[qid].sp]>1) { ans[qid]=max(ans[qid],query(1,1,dfnl[que[qid].sp]-1)); } if(dfnr[que[qid].sp]=0;j--)//用倍增计算sp { int nu=fa[tu][j]; if(dep[nu]=dep[nu]-dep[que[i].f]*2+dep[que[i].v])//sp到u的距离,应该小于sp到v的距离。 { continue; } tu=nu; } que[i].sp=tu; classu[que[i].u].push_back(i);//分类。 classv[que[i].v].push_back(i); } dfs(1); for(int i=1;i<=q;i++) { //printf("%lld\n",ans[i]); io::write(ans[i]); putchar('\n'); } } ``` ### 思维碎片:为什么sp是这么设置的? 当u和v深度不相等时,并且u和v距离为偶数,那么在u、v路径上恰好存在一个点,使得它距离u和v相等。如上图,这个点就是3,而与3连通且不经过u、v路径的其它点(即蓝色部分),去u和v时间都一样。 > 在本文中,“不经过u、v路径”指**边**无重合,不考虑**点**的交叉。 当u和v深度不相等时,并且u和v距离为奇数,那么不存在点到u和v的距离相同。此时,u、v路径上有一条边恰好位于u、v中间,这条边不会有同学走过,而由于u深度更深,所以这条边**下**方的端点(就是sp)一定是靠近u的点,**上**方的端点一定是靠近v的点。这样,sp的子树(包括sp自己)上的同学一定去u点的机房,另外的同学一定去v点的机房。 但当u和v深度相等时,我发现了问题:u、v路径中点恰为f。此时,如果把sp设为f,我们难以向刚才那样划分子树。我们发现,f自己和与f连通却不经过u、v路径的其它点去u和v时间都一样。因此,我们干脆把这些同学让给v,把sp设为f的儿子。 为了简化代码,当u和v深度不相等时,并且u和v距离为偶数时,我们也把sp设为中间点旁边的一个点(在上图中就是4)。 综上,当u和v距离为偶数时,$dis(sp,v)-2=dis(sp,u)>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); update(o); } //线段树其它代码略。 void pre(int u)//预处理dfs序。 { dfnl[u]=++now_dfn;//分配dfs序。 dfnr[u]=dfnl[u]; id[dfnl[u]]=u;//dfs序与点原始编号一一对应。 dep[u]=dep[fa[u][0]]+1; for(int i=head[u];i;i=edge[i].next) { int v=edge[i].to; if(v==fa[u][0]) { continue; } fa[v][0]=u; for(int j=1;j<=MAXF;j++)//构建倍增数组。 { fa[v][j]=fa[fa[v][j-1]][j-1]; } pre(v); dfnr[u]=dfnr[v];//记录本子树最大dfs序。 } } void dfs(int u) { for(int qid:classu[u]) { ans[qid]=max(ans[qid],query(1,dfnl[que[qid].sp],dfnr[que[qid].sp]));//查询sp子树的所有点到本点的最大距离 } for(int qid:classv[u]) { //查询sp子树以外的所有点到本点的最大距离 if(dfnl[que[qid].sp]>1) { ans[qid]=max(ans[qid],query(1,1,dfnl[que[qid].sp]-1)); } if(dfnr[que[qid].sp]