LCA问题,利用并查集和dfs寻找最近公共祖先,挺好理解的。
tarjan算法
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
#define N 10005
bool root[N];
int x,y;
bool v[N];
int set[N],ans[N],n;
int num,to[N],head[N],next[N];
inline void add(int q,int p)
{
to[num]=p;
next[num]=head[q];
head[q]=num++;
}
int find(int x)
{
if(x!=set[x]) set[x]=find(set[x]);
return set[x];
}
void merge(int a,int b)
{
int p=find(a),q=find(b);
if(p==q) return;
set[q]=p;
}
void lca(int k)
{
ans[k]=k;
for(int i=head[k];~i;i=next[i])
{
lca(to[i]);
merge(k,to[i]);
ans[find(to[i])]=k;
}
v[k]=1;
if(k==x)
{
if(v[y])
printf("%d\n",ans[find(y)]);
}
else if(k==y)
{
if(v[x])
printf("%d\n",ans[find(x)]);
}
}
int main()
{
int cas;
cin>>cas;
while(cas--)
{
memset(root,0,sizeof(root));
memset(v,0,sizeof(v));
memset(head,-1,sizeof(head));
num=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) set[i]=i;
for(int i=1;i<n;i++)
scanf("%d%d",&x,&y),add(x,y),root[y]=1;
scanf("%d%d",&x,&y);
for(int i=1;i<=n;i++)
{
if(!root[i])
{
lca(i);
break;
}
}
}
return 0;
}
附上这题的朴素版本,只要一直向上标记就够了。
#include <cstdio>
#include <iostream>
#include<vector>
using namespace std;
int fa[10005];
bool v[10005];
int main()
{
int cas,a,b,n;
scanf("%d",&cas);
while(cas--)
{
scanf("%d",&n);
memset(fa,0,sizeof(fa));
for(int i=1;i<n;i++)
scanf("%d%d",&a,&b),v[i]=0,fa[b]=a;
scanf("%d%d",&a,&b);
while(a)
{
v[a]=1;
a=fa[a];
}
while(!v[b])
{
b=fa[b];
}
printf("%d\n",b);
}
return 0;
}