On the LCA
\ (LCA \) and most recent common ancestor is defined as follows:
Given a rooted tree, if a node \ (Z \) both nodes \ (X \) ancestor node is \ (Y \) of ancestors, it is called \ (x, y \) common ancestor .
In node \ (x, y \) all in the deepest common ancestor is the most recent common ancestor, denoted \ (LCA (the X-, the y-) \) .
\ (LCA \) main role is to find a path between two trees, but there are also some other metaphysical usefulness.
Method to realize
Up labeling
In fact, violent methods.
-
From \ (x \) to the root node to go through and mark all the points.
-
From \ (y \) to go to the root node, when faced with the first point has been marked, the point is \ (LCA (the X-, the y-) \) .
For this method, the worst case time complexity is \ (O (n-) \) .
Tree multiplication method
This is a very important algorithms, in addition to seeking \ (LCA \) , there are very useful.
Pretreatment
Set \ (F [x, k] \) represents \ (X \) a \ (2 ^ k \) generation ancestor, particularly if the point does not exist so that \ (F. [X, K] = 0 \) , the \ (F [x, 0] \) is naturally \ (x \) parent node.
In addition to \ (F [the X-, k] = f [f [the X-, k-1], k-1] \) (similar to dynamic programming), which is the core of this algorithm.
Preprocessing section above, when the code is written again \ (DFS \) to get the time complexity is \ (O (n-\ log \ n-) \) .
You will find that it does not have the above \ (O (n) \) excellent, because this is not a reason for its fast, its fast reflected in the query.
Inquire
When seeking \ (LCA (x, y) \) when divided into the following steps:
- Set \ (d [x] \) represents \ (X \) depth. May assume \ (D [X] \ GEQ D [Y] \) (exchange of two or several).
- Thought resolved with the two binary numbers is adjusted to the same height, specifically, it is to \ (X \) go up \ (k = 2 ^ {log \ n}, ..., 2 ^ 1,2 ^ 0 \) , to check whether the arrival depth ratio \ (Y \) nodes deep, so that if it is \ (X = F. [X, K] \) .
- At this time, if the \ (X == Y \) , described \ (the LCA (X, Y) = Y \) .
- Thought resolved again with the binary \ (X, Y \) , while adjusting up and maintain a consistent depth not meet both, if \ (F [x, k] \ neq F [y, k] \) It will make it \ (X = F. [X, K], Y = F. [Y, K] \) , until \ (F. [X, K] = F. [Y, K] \) .
- At this time, must meet only one step to, \ (F. [X, 0] \) is the \ (the LCA (X, Y) \) .
Time complexity of multiple queries \ (O ((n-m +) log \ n-) \) . Compared above \ (O (nm) \) has indeed been optimized.
Code
The core code is as follows:
void dfs(int x,int fa){
d[x]=d[fa]+1;
f[x][0]=fa;
for(int i=1;(1<<i)<=d[x];i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=edge[i].next){
if(edge[i].to!=fa) dfs(edge[i].to,x);
}
return;
}
//以上是预处理
int LCA(int x,int y){
if(d[x]>d[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
Tarjan algorithm
This section to be even more.
example
Now that we have learned the most basic \ (LCA \) , we come to a deeper understanding of it with a few examples.
Example a
The maximum spanning tree with \ (LCA \) the perfect combination of problems.
First of all truck drivers must take the greatest road, then the connection is clear that only a point and a maximum of two lines of all other points,
Then this constitutes a maximum spanning tree, practices and minimum spanning tree as good, but \ (Kruscal \) descending sorting row.
Then the path between two points with \ (the LCA \) maintenance can.
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define N 10010
#define M 50010
#define INF 999999999
using namespace std;
int n,m,fa[N][30],w[N][30],depth[N];
int p[N],lg[N];
int head[N],cnt=0;
bool vis[N];
struct node1{
int x,y,dis;
}a[M];
struct node2{
int next,to,val;
}edge[N*2];
bool cmp(node1 a,node1 b){
return a.dis>b.dis;
}
int find(int x){
if(x==p[x]) return x;
return p[x]=find(p[x]);
}
void addedge(int x,int y,int z){
cnt++;
edge[cnt].next=head[x];
edge[cnt].to=y;
edge[cnt].val=z;
head[x]=cnt;
return;
}
void kruscal(){
sort(a+1,a+m+1,cmp);
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=m;i++){
int fx=find(a[i].x);
int fy=find(a[i].y);
if(fx==fy) continue;
p[fx]=fy;
addedge(a[i].x,a[i].y,a[i].dis);
addedge(a[i].y,a[i].x,a[i].dis);
}
return;
}
void dfs(int x,int last){
vis[x]=true;
for(int i=head[x];i;i=edge[i].next){
int y=edge[i].to;
if(y==last) continue;
depth[y]=depth[x]+1;
fa[y][0]=x;
w[y][0]=edge[i].val;
dfs(y,x);
}
return;
}
void pre_LCA(){
for(int i=1;i<=n;i++){
if(!vis[i]){
dfs(i,0);
fa[i][0]=i;
w[i][0]=INF;
}
}
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++){
fa[j][i]=fa[fa[j][i-1]][i-1];
w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]);
}
return;
}
int LCA(int x,int y){
if(find(x)!=find(y)) return -1;
int ans=INF;
if(depth[x]<depth[y]) swap(x,y);
for(int i=20;i>=0;i--){
if(depth[fa[x][i]]>=depth[y]){
ans=min(ans,w[x][i]);
x=fa[x][i];
}
}
if(x==y) return ans;
for(int i=20;i>=0;i--){
if(fa[x][i]!=fa[y][i]){
ans=min(ans,min(w[x][i],w[y][i]));
x=fa[x][i],y=fa[y][i];
}
}
return min(ans,min(w[x][0],w[y][0]));
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
lg[i]=lg[i-1]+(1<<lg[i-1]==i);
int u,v,q;
for(int i=1;i<=m;i++){
scanf("%d %d %d",&u,&v,&q);
a[i].x=u;
a[i].y=v;
a[i].dis=q;
}
kruscal();
pre_LCA();
int Q;
scanf("%d",&Q);
while(Q--){
scanf("%d %d",&u,&v);
printf("%d\n",LCA(u,v));
}
return 0;
}
Two examples
[AHOI2008] emergency collection / gathering
This can be considered ... Purple problem?
We can see that the trees three point three pairs \ (LCA \) must have two identical, which is a think about it relatively obvious thing.
Must be able to locate a node, so that two of the three points on one side, one on the other side. And this point is two common \ (LCA \) .
Then some deep thinking (and combined with blind Mongolia), we find that this same \ (LCA \) must have a minimum depth of a \ (LCA \) .
Here, we can first discover the obvious, this point must be accessible to each other in three-point path.
Let us think about \ (LCA \) the relationship between the path and the.
Suppose we know \ (A \) and \ (B \) of \ (the LCA \) is \ (X \) , and \ (X \) is the aforementioned \ (3 \) a \ (the LCA \) maximum depth that.
It can be found from the \ (X \) to \ (A \) is the distance from the plus \ (X \) to \ (B \) distance must be minimal.
According to the above conclusion, we know that \ (A \) , \ (C \) and \ (B \) , \ (C \) a \ (the LCA \) point \ (Y \) constant at a point, and this \ (y \) must be better than \ (x \) small depth.
So this time, we will find that this time \ (A \) , \ (b \) , \ (c \) to \ (x \) distance and is the smallest. Proof, then you can think:
If \ (x '\) than \ (X \) is high, then although the \ (C \) to \ (X \) the distance reduces the \ (W \) , but \ (A \) , \ (B \ ) to \ (x '\) distances are increased \ (W \) , and the distance is clearly increased.
If \ (x '\) than \ (X \) is low, there is a node to \ (x' \) the distance reduces the \ (W \) , the remaining two nodes to \ (x '\) distance They have increased \ (w \) , and obviously the distance increases.
So we found a three-point distance and the minimum point: three point three pairs \ (LCA \) , the large depth of the two \ (LCA \) is the answer.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#define N 500010
using namespace std;
int n,m,head[N],cnt=0;
int d[N],f[N][25];
struct node{
int next,to;
}edge[N<<1];
int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
}
void addedge(int x,int y){
cnt++;
edge[cnt].next=head[x];
edge[cnt].to=y;
head[x]=cnt;
return;
}
void dfs(int x,int fa){
d[x]=d[fa]+1;
f[x][0]=fa;
for(int i=1;(1<<i)<=d[x];i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=edge[i].next){
if(edge[i].to!=fa) dfs(edge[i].to,x);
}
return;
}
int LCA(int x,int y){
if(d[x]>d[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
n=read();m=read();
int x,y,z;
for(int i=1;i<n;i++){
x=read();y=read();
addedge(x,y);addedge(y,x);
}
dfs(1,0);
for(int i=1;i<=m;i++){
x=read();y=read();z=read();
int p1=LCA(x,y);
int p2=LCA(y,z);
int p3=LCA(x,z);
int ans;
if(p1==p2) printf("%d ",p3);
else if(p2==p3) printf("%d ",p1);
else if(p1==p3) printf("%d ",p2);
printf("%d\n",d[x]+d[y]+d[z]-d[p1]-d[p2]-d[p3]);
}
return 0;
}
Epilogue
This ... is over.
Sahua end.
Seemingly forgotten something important:
- From the base part to explain: "algorithm contest Step-up Guide"
- Examples to explain the two parts from this blog
Again Sahua end.