方法一:RMQ
对树做一次dfs序,记录每个节点在dfs序中的初始位置以及每个节点在树中的深度,查询两节点初始位置所形成的区间中深度最小的节点即为两者的最近公共祖先,查询部分采用RMQ即可。
import java.util.*;
public class Main {
static int T,n,ee,a,b,c,kk,rt,ans;
static int[] dd=new int[10005];
static int[] vis=new int[10005];
static int[] pos=new int[10005];
static int[] dep=new int[10005];
static int[] dfs=new int[20005];
static int[] head=new int[10005];
static int[] edge=new int[20005];
static int[] next=new int[20005];
static int[][] min=new int[20005][15];
static void add(int a,int b)
{
edge[ee]=b;next[ee]=head[a];
head[a]=ee++;
}
static void dfs(int x,int d)
{
dep[x]=d;vis[x]=1;
dfs[++kk]=x;pos[x]=kk;
for(int i=head[x];i!=-1;i=next[i])
{
int v=edge[i];
if(vis[v]==1) continue;
dfs(v,d+1);
dfs[++kk]=x;
}
}
static int rmq(int l,int r)
{
c=0;
while((1<<(c+1))<=r-l+1) c++;
int res=0;
if(dep[min[l][c]]<dep[min[r-(1<<c)+1][c]])
res=min[l][c];
else res=min[r-(1<<c)+1][c];
return res;
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
T=in.nextInt();
while(T-->0)
{
n=in.nextInt();
Arrays.fill(head,-1);ee=0;
Arrays.fill(dd,0);
for(int i=1;i<n;i++)
{
a=in.nextInt();b=in.nextInt();
add(a,b);dd[b]++;
}
Arrays.fill(vis,0);kk=0;
for(int i=1;i<=n;i++)
if(dd[i]==0) { rt=i;break;}
dfs(rt,1);
for(int j=0;j<=14;j++)
for(int i=1;i<=kk;i++)
{
if(i+(1<<j)-1>kk) continue;
if(j==0) min[i][j]=dfs[i];
else
{
if(dep[min[i][j-1]]<dep[min[i+(1<<(j-1))][j-1]])
min[i][j]=min[i][j-1];
else min[i][j]=min[i+(1<<(j-1))][j-1];
}
}
a=in.nextInt();b=in.nextInt();
if(pos[a]>pos[b]) { c=a;a=b;b=c;}
ans=rmq(pos[a],pos[b]);
System.out.println(ans);
}
}
}
方法二:倍增法
import java.util.*;
public class Main {
static int T,n,a,b,c,ee,rt,ans;
static int[] dd=new int[10005];
static int[] dep=new int[10005];
static int[] head=new int[10005];
static int[] edge=new int[20005];
static int[] next=new int[20005];
static int[][] ff=new int[10005][15];
static void add(int a,int b)
{
edge[ee]=b;next[ee]=head[a];
head[a]=ee++;
}
static void dfs(int x,int fa,int d)
{
ff[x][0]=fa;dep[x]=d;
for(int i=1;(1<<i)<d;i++)
ff[x][i]=ff[ff[x][i-1]][i-1];
for(int i=head[x];i!=-1;i=next[i])
dfs(edge[i],x,d+1);
}
static int LCA(int u,int v)
{
if(dep[u]<dep[v]) { c=v;v=u;u=c;}
for(int i=14;i>=0;i--)
if(dep[u]-(1<<i)>=dep[v]) u=ff[u][i];
for(int i=14;i>=0;i--)
{
if(dep[u]<=(1<<i)||ff[u][i]==ff[v][i]) continue;
u=ff[u][i];v=ff[v][i];
}
int res=(u==v)?u:ff[u][0];
return res;
}
public static void main(String[] args) {
Scanner in=new Scanner(System.in);
T=in.nextInt();
while(T-->0)
{
n=in.nextInt();
Arrays.fill(head,-1);ee=0;
Arrays.fill(dd,0);
for(int i=1;i<n;i++)
{
a=in.nextInt();b=in.nextInt();
add(a,b);dd[b]++;
}
for(int i=1;i<=n;i++)
if(dd[i]==0) { rt=i;break;}
dfs(rt,-1,1);
a=in.nextInt();b=in.nextInt();
ans=LCA(a,b);
System.out.println(ans);
}
}
}