题目:
给定一棵
n
n
n个结点的树,和
m
m
m次询问,每次询问给定
x
,
y
,
z
x,y,z
x,y,z,表示三个不同的结点,求结点
p
p
p满足
d
=
d
i
s
(
x
,
p
)
+
d
i
s
(
y
,
p
)
+
d
i
s
(
z
,
p
)
d=dis(x,p)+dis(y,p)+dis(z,p)
d=dis(x,p)+dis(y,p)+dis(z,p)最小化,并输出
d
d
d。
(
1
≤
n
≤
500000
,
1
≤
x
,
y
,
z
≤
n
)
(1 \le n \le 500000,1 \le x,y,z \le n)
(1≤n≤500000,1≤x,y,z≤n)
题解:
一道分类讨论题。
-
存在 x , y x,y x,y满足 x x x是 y y y的祖先
那么可能的情况有
-
不存在 x , y x,y x,y满足 x x x是 y y y的祖先
由于 x , y , z x,y,z x,y,z是同等地位的,所以具有轮换性,我们可以用过适当的 s w a p swap swap将字母变成和上述两种情况相同来减少分类讨论的难度。
由于点数比较大,用倍增 l c a lca lca的复杂度为 O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn),过不去,需要用ST表的 l c a lca lca
复杂度: O ( ( n l o g n + m ) O((nlogn+m) O((nlogn+m)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int maxn=5e5+5;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int tot,n,m,cnt;
int dep[maxn],ver[2*maxn],fir[maxn],st[maxn*2][21],logn[maxn*2];
struct edge{
int to,nxt;
}es[maxn*2];
int head[maxn];
void init(){
memset(head,-1,sizeof(head));
tot=cnt=0;
logn[0]=-1;
for(int i=1;i<=2*n;i++)logn[i]=logn[i>>1]+1;
}
void addEdge(int u,int v){
es[tot].to=v;
es[tot].nxt=head[u];
head[u]=tot++;
}
void dfs(int u,int fa,int d){
dep[u]=d;
ver[++cnt]=u;
fir[u]=cnt;
for(int i=head[u];i!=-1;i=es[i].nxt){
int v=es[i].to;
if(v==fa)continue;
dfs(v,u,d+1);
ver[++cnt]=u;
}
}
void Init(){
for(int i=1;i<=cnt;i++){
st[i][0]=fir[ver[i]];
}
for(int j=1;(1<<j)<=cnt;j++){
for(int i=1;i+(1<<j)-1<=cnt;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
int qry(int l,int r){
int k=logn[r-l+1];
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int lca(int u,int v){
int l=fir[u],r=fir[v];
if(l>r)swap(l,r);
return ver[qry(l,r)];
}
int ck(int a,int b,int c){
int t1=lca(a,b),t2=lca(a,c),t3=lca(b,c),f=1;
if(t1==a){
;
}
else if(t1==b){
swap(a,b);
}
else if(t2==a){
swap(b,c);
}
else if(t2==c){
swap(a,c);
swap(b,c);
}
else if(t3==b){
swap(a,b);
swap(b,c);
}
else if(t3==c){
swap(a,c);
}
else{
f=0;
}
t1=lca(a,b),t2=lca(a,c),t3=lca(b,c);
if(f){
if(t2==c){
return a;
}
else if(t2==a){
if(t3==b){
return b;
}
else{
return t3;
}
}
else{
return a;
}
}
else{
if(t2==t3){
return t1;
}
else{
if(t1==t2){
return t3;
}
else{
return t2;
}
}
}
}
int dis(int u,int v){
return dep[u]+dep[v]-2*dep[lca(u,v)];
}
int calc(int a,int b,int c,int u){
// printf("%d %d %d\n",dis(a,u),dis(b,u),dis(c,u));
return dis(a,u)+dis(b,u)+dis(c,u);
}
int main(void){
// freopen("in.txt","r",stdin);
n=read();m=read();
init();
int a,b;
for(int i=1;i<=n-1;i++){
a=read();b=read();
addEdge(a,b);
addEdge(b,a);
}
dfs(1,0,1);
Init();
int x,y,z;
while(m--){
x=read();y=read();z=read();
int p=ck(x,y,z),d=calc(x,y,z,p);
printf("%d %d\n",p,d);
}
return 0;
}