BZOJ-1832 [AHOI2008]聚会(LCA)
题目描述
有一棵 \(n(1\leq n\leq 5\times 10^5)\) 个点,\(n-1\) 条边的树,\(q(1\leq q\leq 5\times 10^5)\) 次询问,每次询问给出三个点 \(a,b,c\),在树上选择一个点 \(x\),使得 \(a,b,c\) 三点走到点 \(x\) 的距离之和最小,输出点 \(x\) 的编号和最小权值和。
分析
三点的三个 \(\text{LCA}\) 分为两种情况:
\(1.\) 三个 \(\text{LCA}\) 重合在同一点,此时重合点就是 \(x\)。
\(2.\) 两个 \(\text{LCA}\) 重合在同一点,另一个 \(\text{LCA}\) 孤立,此时孤立的 \(\text{LCA}\) 是 \(x\)。
对于这两种情况,距离之和都是 \((dis(a,b)+dis(b,c)+dis(a,c))/2\)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int head[N],num_edge;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
struct Edge
{
int to;
int Next;
int dis;
}edge[N<<1];
void add_edge(int from,int to,int dis)
{
edge[++num_edge].to=to;
edge[num_edge].dis=dis;
edge[num_edge].Next=head[from];
head[from]=num_edge;
}
int n,q,_log;
int f[N][20],dep[N]/*,dist[N]*/;
queue<int> Q;
void bfs()
{
Q.push(1);
dep[1]=1;
while(!Q.empty())
{
int x=Q.front();
Q.pop();
for(int i=head[x];i;i=edge[i].Next)
{
int y=edge[i].to;
if(dep[y])
continue;
dep[y]=dep[x]+1;
//dist[y]=dist[x]+1;
f[y][0]=x;
for(int j=1;j<=_log;j++)
f[y][j]=f[f[y][j-1]][j-1];
Q.push(y);
}
}
}
int LCA(int x,int y)
{
if(dep[x]>dep[y])
swap(x,y);
for(int i=_log;i>=0;i--)
if(dep[f[y][i]]>=dep[x])
y=f[y][i];
if(x==y)
return x;
for(int i=_log;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
long long dis(int x,int y)
{
return (long long)(dep[x]+dep[y]-2*dep[LCA(x,y)]);
}
int main()
{
cin>>n>>q;
_log=(int)(log(n)/log(2))+1;
for(int i=1;i<=n-1;i++)
{
int x=read(),y=read();
add_edge(x,y,1);
add_edge(y,x,1);
}
bfs();
while(q--)
{
int a=read(),b=read(),c=read(),x;
if(LCA(a,b)==LCA(b,c))
x=LCA(a,c);
else if(LCA(a,b)==LCA(a,c))
x=LCA(b,c);
else if(LCA(b,c)==LCA(a,c))
x=LCA(a,b);
printf("%d %lld\n",x,(dis(a,b)+dis(b,c)+dis(a,c))/2);
}
return 0;
}
posted on 2020-11-18 23:59 DestinHistoire 阅读(108) 评论(0) 收藏 举报
浙公网安备 33010602011771号