最近公共祖先
Time Limit:10000MS Memory Limit:65536K
Total Submit:104 Accepted:91
Case Time Limit:1000MS
Description
给出一棵有N(编号1到N)个节点的有根树,求出指定节点对的最近公共祖先!
对于树中节点x而言,从根节点到达x的这一条路径中经过的所有节点,都称为x的祖先。
如上图所表示的树中, 根节点为8。8、4、10、16都是12的祖先。对于6和12这对节点而言,从6出发往上朝根走和从12出发往上朝根走的两条路径最早交汇的地点是4号节点,因此4号点是6和12的最近公共祖先。
同理,11和9的最近公共祖先是8; 10和3的最近公共祖先是10;2和7的最近公共祖先是4......
Input
第一行,一个整数N。表示树中节点总数
接下来N-1行,每行两个整数x和y,表示x是y的父亲。
接下来一行,一个整数M,表示询问的总数
接下来M行,每行两个整数a和b,表示询问a和b的最近公共祖先。
Output
M行,每行一个整数,表示对应询问的答案。
Sample Input
输入样例1:
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
3
16 7
14 9
3 10
输入样例2:
5
2 3
3 4
3 1
1 5
2
3 5
4 5
Sample Output
输出样例1:
4
8
10
输出样例2:
3
3
Hint
2<=N<=10000
1<=M<=10000
Source
改编自POJ1330
求有关最近公共祖先的问题可以用LCA来解决,与倍增有关,是比较实用的技巧
树上倍增算法:
Dep[v]记录节点v的深度(层数)
fa[v][k]记录节点v向上第2^k个祖先的编号
fa[v][k]=fa[fa[v][k-1]][k-1];
s为倍增的上限,比如这个树的总层数为8,则s为3(23==8)
然后对于两个不同的节点A,B我们要求他们的公共祖先,首先要用到go_up函数(在本人代码中将其和lca函数合在了一起)使层数较大的一个节点转换到一个层数与另一节点相同的祖先去,下面是代码
#include<cstdio>
#include<iostream>
#include<cmath>
using namespace std;
const int maxn=10005;
int n,quest,a,b,cnt,depth[maxn],fa[maxn][35],m;
int NEXT[maxn<<1],END[maxn<<1],last[maxn];
bool mark[maxn];
inline void _read(int &x){
char t=getchar();bool sign=true;
while(t<'0'||t>'9')
{if(t=='-')sign=false;t=getchar();}
for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0';
if(!sign)x=-x;
}
void insert(int a,int b){
END[++cnt]=b;
NEXT[cnt]=last[a];
last[a]=cnt;
}
void dfs(int v){<span style="font-family:Times New Roman;">//深搜建树</span>
int i,j,k;
depth[v]=depth[fa[v][0]]+1;
k=ceil(log(depth[v])/log(2));//k为当前讨论的两个节点的倍增值<span style="font-family:Times New Roman;">上限</span>
for(i=1;i<=k;i++)
fa[v][i]=fa[fa[v][i-1]][i-1];//递推计算fa数组
j=last[v];
while(j){
dfs(END[j]);
j=NEXT[j];
}
}
int lca(int x,int y)
{
int i,k,s;
s=ceil(log(n)/log(2));
if(depth[x]<depth[y])swap(x,y);
k=depth[x]-depth[y];
for(i=0;i<=s;i++)
if(k&(1<<i))x=fa[x][i];//go_up操作
if(x==y)return x;
s=ceil(log(depth[x])/log(2));
for(i=s;i>=0;i--)
if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; }
return fa[x][0];//最终返回公共祖先节点
}
int main(){
_read(n);
for(int i=1;i<n;i++){
_read(a);_read(b);
insert(a,b);
fa[b][0]=a;//预处理fa数组
}
for(int i=1;i<=n;i++)
if(!fa[i][0]){
dfs(i);
break;
}
_read(m);
for(int i=1;i<=m;i++){
_read(a);_read(b);
printf("%d\n",lca(a,b));
}
}