最近公共祖先
前置芝士
倍增算法
本质思想:可以通过二进制数任意组合拼凑出任意一个正整数
最近公共祖先(LCA)
[problem description]
如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。
[input]
第一行包含三个正整数 \(N,M,S\),分别表示树的结点个数、询问的个数和树根结点的序号。
接下来 \(N-1\) 行每行包含两个正整数 \(x, y\),表示 \(x\) 结点和 \(y\) 结点之间有一条直接连接的边(数据保证可以构成树)。
接下来 \(M\) 行每行包含两个正整数 \(a, b\),表示询问 \(a\) 结点和 \(b\) 结点的最近公共祖先。
[output]
输出包含 \(M\) 行,每行包含一个正整数,依次为每一个询问的结果。
[sample]
in
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5
out
4
4
1
4
4
\(1 \leq N,M\leq 500000\),\(1 \leq x, y,a ,b \leq N\),不保证 \(a \neq b\)
[solved]
倍增法求解
const int N=500010;
int dep[N],pa[N][22];
vector<int> e[N];
int n,m,s,a,b;
void dfs(int x,int fa){
dep[x]=dep[fa]+1;
pa[x][0]=fa;
for(int i=1;i<=20;i++){
pa[x][i]=pa[pa[x][i-1]][i-1];
}
for(int y:e[x]){
if(y!=fa) dfs(y,x);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--){
if(dep[pa[x][i]]>=dep[y]) x=pa[x][i];
}
if(x==y) return y;
for(int i=20;i>=0;i--){
if(pa[x][i]!=pa[y][i]) x=pa[x][i],y=pa[y][i];
}
return pa[x][0];
}
void solve() {
cin>>n>>m>>s;
for(int i=1,x,y;i<n;i++){
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(s,0);
for(int i=1,x,y;i<=m;i++){
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
}
Tarjan(塔扬)
是一种离线算法,巧妙利用并查集维护祖先节点