最近公共祖先(倍增LCA)
题目
描述
给定一棵树,请查询结点u和v的最近公共祖先。最近公共祖先,就是两个节点在这棵树上深度最大(离根结点最远)的公共的祖先节点,结点的祖先也可以是自身。
输入
输入数据第一行为结点个数n(n<=900),接下来有n行,每行格式如下:
x m y1, y2, ... ym
表示结点x有m个孩子y1, y2, ... ym
接下来有一个正整数q(q<=100000),表示查询次数,之后有q行,每行两个正整数u和v,表示待查寻的结点编号。
树结点的编号为1~n。
输出
对每次查询,输出u和v的最近公共祖先,格式为:
u v = w
其中w表示u和v的最近公共祖先结点编号。
样例输入
5
4 0
5 3 1 4 2
1 0
2 1 3
3 0
6
1 5
1 4
4 2
2 3
1 3
4 3
样例输出
1 5 = 5
1 4 = 5
4 2 = 5
2 3 = 2
1 3 = 5
4 3 = 5
代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
const int N=1100;
int fa[N][N],depth[N];
int h[N],e[N*2],ne[N*2],idx; //链式前向星无向,边数得乘2
int d[N],root;
void add(int x,int y) //链式前向星建边
{
e[idx]=y;
ne[idx]=h[x];
h[x]=idx++;
}
void bfs(int u) //广搜预处理fa数组和depth数组
{
memset(depth,0x3f,sizeof(depth));
depth[0]=0,depth[root]=1;
queue<int>q;
q.push(root);
while(!q.empty()) {
int t=q.front();
q.pop();
for(int i=h[t];i!=-1;i=ne[i]) {
int j=e[i];
if(depth[j]>depth[t]+1) { //广搜预处理depth数组
depth[j]=depth[t]+1;
q.push(j);
fa[j][0]=t; //fa[j][0]表示父亲
for(int k=1;k<=15;k++) {
fa[j][k]=fa[fa[j][k-1]][k-1]; //f(i,k)=f(f(i,k-1),k-1)推导处理fa数组
}
}
}
}
}
int lca(int a,int b)
{
if(depth[a]<depth[b]) swap(a,b); //如果a比b浅,交换a,b
for(int i=15;i>=0;i--) { //1.将a和b调到同一层
if(depth[fa[a][i]]>=depth[b])
a=fa[a][i];
}
if(a==b) return a; //如果调换以后相等则b为最近祖先
for(int i=15;i>=0;i--) { //否则a和b一起向上跳,一直到最近祖先的儿子结点
if(fa[a][i]!=fa[b][i]) {
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int main()
{
int n;
cin>>n;
memset(h,-1,sizeof(h));
for(int i=1;i<=n;i++) {
int x,m;
cin>>x>>m;
while(m--) {
int xx;
cin>>xx;
d[xx]++;
add(x,xx);
add(xx,x);
}
}
for(int i=1;i<=n;i++) if(d[i]==0) root=i; //入度为0的点作为根结点
// cout<<root<<endl;
bfs(root);
int m;
cin>>m;
while(m--) {
int a,b;
cin>>a>>b;
int t=lca(a,b);
cout<<a<<" "<<b<<" = "<<t<<endl;
}
// system("pause");
return 0;
}