题目大意:给一棵树,给定两个点,找到他们的最近公共祖先
题目分析:这个题目的查询只有一次,我们只需要用最暴力的方法去完成就可以了
题目详解:
给定一棵树,我们想要找到他的最近公共祖先,我们用一个数组来记录每个结点的父亲结点。那么我们要想找的最近公共祖先,一定在该结点的祖先结点及其本身之内(祖先结点就是该点的父结点和父结点的父结点及向上找所有的父结点)。
那么对于两个点,我们只需要用dfs遍历的时候记录这两个点的深度,让深度大的那个点不断沿着父结点向上找,一直到深度相同位置(因为两个点的公共祖先是同一个点,深度一定相同),然后判断这两个是否是同一个点,如果是就一定找到,如果不是,两个点同时向上找,找到的第一个点就是最近公共祖先了
代码:
#include <bits/stdc++.h>
#define cl(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn=1e5+50;
const int mod=1e9+7;
const int inf=0x3f3f3f3f;
typedef long long ll;
int flag[maxn];//深度遍历时用于标记节点是否被访问
int _father[maxn];//标记每个节点的父亲节点
int depth[maxn];//标记每个节点的深度
vector<int>g[maxn];
void dfs(int s,int step)
{
depth[s]=step;
flag[s]=1;
for(int i=0; i<g[s].size(); i++)
{
int k=g[s][i];
if(!flag[k])
{
dfs(k,step+1);
}
}
}
int findlca(int u,int v)
{
int fu=u;
int fv=v;
int depu=depth[fu];
int depv=depth[fv];
while(depu<depv)
{
fv=_father[fv];
depv=depth[fv];
}
while(depu>depv)
{
fu=_father[fu];
depu=depth[fu];
}
while(fv!=fu)
{
fv=_father[fv];
fu=_father[fu];
}
return fv;
}
void init()
{
cl(_father);
cl(flag);
cl(depth);
for(int i=0; i<maxn; i++)
{
g[i].clear();
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m;
int T;
cin>>T;
while(T--)
{
init();
cin>>n;
for(int i=1; i<n; i++)
{
int x,y;
cin>>x>>y;
g[x].push_back(y);
_father[y]=x;
}
int root=0;
for(int i=1;i<=n;i++)
{
if(_father[i]==0)root = i;
}
dfs(root,1);
int u,v;
cin>>u>>v;
cout<<findlca(u,v)<<endl;
}
return 0;
}