Description
求树上一点到给定的三点 a , b , c a,b,c a,b,c 的最小值
Solution
我们从
n
=
2
n=2
n=2 开始:
两个人回合,树上有唯一简单路径(路径上的每一点都可以)
再看
i
=
3
i=3
i=3 :
显然不能直接让两个和 第三人 会合。
我们要考虑
i
,
j
,
k
i, j,k
i,j,k 的位置关系
简单画个图,应该能发现3条(两两连接的)路径有重合。进一步发现有两对点的LCA一致,另一个LCA的
d
e
p
dep
dep更深
设
i
,
j
i, j
i,j 在深度更深的
d
e
p
i
,
j
dep_{i, j}
depi,j 会合,之后应该让
k
k
k 走向
d
e
p
i
,
j
dep_{i, j}
depi,j ,原因:
k
k
k 单独走一步,或
i
,
j
i, j
i,j一起走两步,显然
k
k
k走更好
那么我们求出LCA,比较出相同LCA来排出更深的LCA即为会合点(上面的原因解释了为什么这样最优)
设三人会合点
g
g
g
式子:
a
n
s
=
d
e
p
[
a
]
+
d
e
p
[
b
]
+
d
e
p
[
c
]
−
d
e
p
[
l
a
b
]
−
d
e
p
[
l
b
c
]
−
d
e
p
[
l
a
c
]
ans=dep[a]+dep[b]+dep[c]-dep[lab]-dep[lbc]-dep[lac]
ans=dep[a]+dep[b]+dep[c]−dep[lab]−dep[lbc]−dep[lac]
如下图:
a
n
s
=
(
d
e
p
[
a
]
−
d
e
p
[
l
a
c
]
)
+
(
d
e
p
[
b
]
−
d
e
p
[
l
a
b
]
)
+
(
d
e
p
[
c
]
−
d
e
p
[
l
b
c
]
)
ans=(dep[a]-dep[lac])+(dep[b]-dep[lab])+(dep[c]-dep[lbc])
ans=(dep[a]−dep[lac])+(dep[b]−dep[lab])+(dep[c]−dep[lbc])
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll read(){
ll res=0, f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
while(ch>='0'&&ch<='9') {res=(res<<3)+(res<<1)+ch-48; ch=getchar();}
return res*f;
}
const ll inf=0x3f3f3f3f3f3f3f3f, maxn=7e5+5;
ll n, m;
ll hd[maxn];
struct Edge{
ll to, nxt;
} e[maxn<<1];
void add(ll u, ll v, ll i){
ll p=i*2, q=i*2-1;
e[p].to=v, e[p].nxt=hd[u], hd[u]=p;
e[q].to=u, e[q].nxt=hd[v], hd[v]=q;
}
ll dep[maxn], f[maxn][23];
void dfs(ll u, ll fa){
dep[u]=dep[fa]+1;
for(ll i = hd[u]; i ; i=e[i].nxt){
ll v=e[i].to;
if(v==fa) continue;
f[v][0]=u;
for(ll j = 1; j <= 22 ; j++) f[v][j]=f[f[v][j-1]][j-1];
dfs(v, u);
}
}
ll LCA(ll x, ll y){//倍增LCA
if(dep[x]<dep[y]) swap(x, y);
for(ll i = 22; i >= 0 ; i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(ll i = 22; i >= 0 ; i--)
if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
return f[x][0];
}
int main(){
// freopen(".in", "r", stdin);
// freopen(".in", "r", stdin);
n=read(), m=read();
for(ll i = 1; i < n ; i++){
ll u=read(), v=read();
add(u, v, i);
}
dfs(1, 0);
for(ll i = 1; i <= m ; i++){
ll ans=inf, num=0;
ll a=read(), b=read(), c=read();
ll lab=LCA(a, b), lbc=LCA(b, c), lac=LCA(c, a);
ll d, s=dep[a]+dep[b]+dep[c];
if(lab==lbc) d=lac;//确定(排除出)三人会合点(较低的、与其他两者不同的lca)
else if(lab==lac) d=lbc;
else d=lab;
ans=s-dep[lab]-dep[lbc]-dep[lac];//计算路径和
printf("%lld %lld\n", d, ans);
}
return 0;
}