On the LCA

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.

  1. From \ (x \) to the root node to go through and mark all the points.

  2. 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:

  1. Set \ (d [x] \) represents \ (X \) depth. May assume \ (D [X] \ GEQ D [Y] \) (exchange of two or several).
  2. 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] \) .
  3. At this time, if the \ (X == Y \) , described \ (the LCA (X, Y) = Y \) .
  4. 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] \) .
  5. 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

Template title

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

Trucking

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:

  1. From the base part to explain: "algorithm contest Step-up Guide"
  2. Examples to explain the two parts from this blog

Again Sahua end.

Guess you like

Origin www.cnblogs.com/lpf-666/p/12606460.html
lca