3824. 【NOIP2014模拟9.9】渴
Description
给定一个 nnn 个点的树,后有 mmm 次询问,每次给出两个点 x,yx,yx,y ,问 max(min(disi,x,disi,y))i∈[1,n]andi≠x,y\max(\min( dis_{i,x} ,dis_{i,y} )) i \in [1,n] and i \not= x,ymax(min(disi,x,disi,y))i∈[1,n]andi=x,y
Solution
小范围内暴力做,大范围内直接拿直径端点判
按理来说大范围的乱搞虽然是错的,但是链的分应该拿到啊。
很奇怪反正。
后来想想如果 x,yx,yx,y 两个点都偏向端点,那么最远的点可能是中间的,所以会错。要把链的分拿到还是要单独拿出来打。
其实是好做的只是当时想简单了,痛失 10 pts 啊。
现在会了
简单写一写吧
首先,到某个点最长的路径一定与树的直径部分重叠,因为最远的点一定是直径的某个端点。
那么我们首先找出树的直径,再从直径的每个点出发 dfs ,要求不经过直径上的其他点,这样就可以得出其他点到直径上哪个点(指在直径上第几个)最近,以及距离。
现在我们输入两个数 x,yx,yx,y ,令 xxx 离直径上的第 tpxtpxtpx 个点最近,他们之间的距离为 dsxdsxdsx ( yyy 同理)。
(这里默认 tpx<tpytpx<tpytpx<tpy )
那么现在我们可以找到从 x∼yx \sim yx∼y 的一条路径(实际上在树上这是唯一的),也知道这条路径经过直径的部分( tpx∼tpytpx \sim tpytpx∼tpy )。算出这条路径中点在直径上的位置 mid=tpx+tpy+dsy−dsx2mid=\frac{tpx+tpy+dsy-dsx}{2}mid=2tpx+tpy+dsy−dsx (一定在直径上),实际上在 midmidmid 左边的点一定离 xxx 更近,另一边离 yyy 更近。
现在分四种情况讨论最远的点所在的位置:
- 直径的左端点
- 直径的右端点
- 在直径上 tpx∼midtpx \sim midtpx∼mid 的这一段中,某一点 iii 向下延申找到一个极深点,向上走回直径,走直径上第 i∼tpxi \sim tpxi∼tpx ,再走到 xxx
- 在直径上 mid∼tpymid \sim tpymid∼tpy 的这一段中,某一点 iii 向下延申找到一个极深点,向上走回直径,走直径上第 i∼tpyi \sim tpyi∼tpy ,再走到 yyy
前两种情况是好做的,后两种需要一些处理。
我们现在需要求的就是直径上某一段向下最深的点,其实这个是从头到尾不会改变的,可以考虑使用 st 表维护。
发现我们在前面将直径上每个点作为起点 dfs 的时候,可以顺势求出在它可以遍历到的范畴内离它最远的点,记直径上第 iii 个点向下遍历最深的点到直径左端点的距离为 f0,if_{0,i}f0,i ,到右端点则为 g0,ig_{0,i}g0,i 。采用端点是因为这样比较的基准是一样的。
然后前面的东西我们就可以完成啦。
注意判断 x,yx,yx,y 两点在到直径最近的点是同一个的情况。
Code
c++
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int N=2e5,LGN=log2(N);
struct edge
{
int to,nxt;
}e[2*N+5];
int lst[N+5],cnt;
int dep[N+5],fa[N+5];
int tp[N+5];
bool bz[N+5];
int a[N+5];
int f[2][LGN+5][N+5];
int dmx;
int n,lgn;
void ad(int x,int y)
{
e[++cnt]=(edge){y,lst[x]};
lst[x]=cnt;
}
void dfs(int x,int y,int z)
{
dep[x]=dep[y]+1,fa[x]=y,tp[x]=z;
if(dep[x]>dep[dmx])
{
dmx=x;
}
for(int i=lst[x];i;i=e[i].nxt)
{
if((e[i].to!=y)&&(!bz[e[i].to]))
{
dfs(e[i].to,x,z);
}
}
}
void st()
{
int lg=log2(a[0]);
for(int i=1;i<=lg;i++)
{
for(int j=1;(j+(1<<i)-1)<=a[0];j++)
{
for(int k=0;k<=1;k++)
{
f[k][i][j]=max(f[k][i-1][j],f[k][i-1][j+(1<<(i-1))]);
}
}
}
}
int qry(int opt,int l,int r)
{
if(l>r)
{
return 0;
}
int t1=log2(r-l+1);
return max(f[opt][t1][l],f[opt][t1][r-(1<<t1)+1]);
}
int main()
{
// freopen("thirst1.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
ad(u,v),ad(v,u);
}
dfs(1,0,0);
int de1=dmx;
dmx=0;
dfs(de1,0,0);
int de2=dmx;
for(int i=de2;i!=de1;i=fa[i])
{
bz[i]=1,a[++a[0]]=i;
}
bz[de1]=1,a[++a[0]]=de1;
for(int i=1;i<=a[0];i++)
{
// printf(" %d %d\n",i,a[i]);
dmx=0;
dfs(a[i],0,i);
f[0][0][i]=i-1+dep[dmx]-1,f[1][0][i]=a[0]-i+dep[dmx]-1;
}
// printf("%d %d %d\n",de1,de2,a[0]);
st();
int m;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int tpx=tp[x],tpy=tp[y];
if(tpx>tpy)
{
swap(tpx,tpy),swap(x,y);
}
int dsx=dep[x],dsy=dep[y];
// printf(" %d %d %d %d\n",tpx,tpy,dsx,dsy);
int mid=(tpx+tpy+dsy-dsx)/2;
printf("%d\n",(tpx==tpy)?(max(tpx-1,a[0]-tpx)+min(dsx,dsy)-1):max(max(min(dsx-1+tpx-1,dsy-1+tpy-1),min(dsx-1+a[0]-tpx,dsy-1+a[0]-tpy)),max(qry(0,tpx+1,mid)+dsx-1-(tpx-1),qry(1,mid+1,tpy-1)+dsy-1-(a[0]-tpy))));
}
return 0;
}
Thanks
码字不易,请多支持😘
如有疏漏,欢迎评论区留言或者私信,我会及时调整更正。
谢谢啦🍝