题意
给定一棵树(N<=500000),多次询问(M<=500000):找出一个点到给定3个点的距离最小。
思路
先考虑如果是两个点的话,答案显然可以是它们的
lca
l
c
a
。
可以猜测答案可能跟
3
3
个点的有关。
对于树上任意不同的
3
3
个点,可以产生总共个点对,其中
lca
l
c
a
只有
2
2
种。
证明:
考虑有两个点,
lca(x,y)=a
l
c
a
(
x
,
y
)
=
a
,且
a=x
a
=
x
,现在加一个新的
z
z
,如果不属于
a
a
的子树,那显然,如果
z
z
属于的子树,可以归为下面的情况。
考虑现在有两个点
x
x
和,
lca(x,y)
l
c
a
(
x
,
y
)
为
a
a
,且,
a≠y
a
≠
y
,现在我们把另一个点
z
z
加进去,对于来说,
z
z
可以划分出两种状态,一种是属于
a
a
的子树,另一种不属于。
对于不属于
a
a
的子树时,可以得到所以
lca(y,z)=lca(x,z)
l
c
a
(
y
,
z
)
=
l
c
a
(
x
,
z
)
。
当 z z 属于的子树时,假设把 a a 去掉,出现了许多的树,又有了两种情况,一种是不属于 x x 或所代表子树中,另一种 z z 属于或 y y 代表的子树里,第一种情况,,对于第二种情况假设 z z 属于代表的子树里,那么 lca(x,y)=lca(z,y)=a l c a ( x , y ) = l c a ( z , y ) = a 。
证毕。
答案一定在
2
2
个之中
证明:
对于任意一个不与两个
lca
l
c
a
重合的点,考虑最好情况下,
3
3
个点到这个点的路径上一定至少有两个点会经过,且会经过另外
1
1
个点,答案显然比在大。
答案为深度大的
lca
l
c
a
考虑到如果选择深度小的
lca
l
c
a
会使有的点做无用功,则答案一定为深度大的
lca
l
c
a
,即
3
3
个中出现
1
1
<script type="math/tex" id="MathJax-Element-16572">1</script>的那个。
#include<cstdio>
const int N=500005;
const int M=500005;
const int BIT=19;
struct edge{
int to,nx;
}e[M<<1];
int h[N],tot,fa[20][N],dep[N],n,m;
void swap(int &x,int &y){
int t=x;
x=y;
y=t;
}
void ins(int x,int y){
e[++tot]=(edge){y,h[x]};
h[x]=tot;
}
void lk(int x,int y){
ins(x,y);
ins(y,x);
}
void dfs(int x,int y){
fa[0][x]=y;
dep[x]=dep[y]+1;
for(int i=h[x];i;i=e[i].nx){
if(e[i].to==y)continue;
dfs(e[i].to,x);
}
}
void init(){
for(int i=1;i<=BIT;i++){
for(int j=1;j<=n;j++){
fa[i][j]=fa[i-1][fa[i-1][j]];
}
}
}
int getlca(int x,int y){
if(dep[y]>dep[x])swap(x,y);
int delta=dep[x]-dep[y];
for(int i=0;i<BIT;i++){
if(delta&(1<<i)){
x=fa[i][x];
}
}
if(x==y)return x;
for(int i=BIT;i>=0;i--){
if(fa[i][x]!=fa[i][y]){
x=fa[i][x];
y=fa[i][y];
}
}
return fa[0][x];
}
int calc(int a,int b,int c,int d){
int ret=0;
int t=getlca(a,d);
ret += dep[d]+dep[a]-2*dep[t];
t=getlca(b,d);
ret+=dep[d]+dep[b]-2*dep[t];
t=getlca(c,d);
ret+=dep[c]+dep[d]-2*dep[t];
return ret;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
lk(a,b);
}
dfs(1,0);
init();
for(int i=1;i<=m;i++){
int a,b,c,d,e,f;
scanf("%d%d%d",&a,&b,&c);
d=getlca(a,b);
e=getlca(a,c);
f=getlca(b,c);
if(d==e){
printf("%d %d\n",f,calc(a,b,c,f));
}else if(d==f){
printf("%d %d\n",e,calc(a,b,c,e));
}else{
printf("%d %d\n",d,calc(a,b,c,d));
}
}
return 0;
}