1. 程式人生 > >1151 LCA in a Binary Tree (30 分)

1151 LCA in a Binary Tree (30 分)

The lowest common ancestor (LCA) of two nodes U and V in a tree is the deepest node that has both U and V as descendants.

Given any two nodes in a binary tree, you are supposed to find their LCA.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers: M (≤ 1,000), the number of pairs of nodes to be tested; and N (≤ 10,000), the number of keys in the binary tree, respectively. In each of the following two lines, N distinct integers are given as the inorder and preorder traversal sequences of the binary tree, respectively. It is guaranteed that the binary tree can be uniquely determined by the input sequences. Then M lines follow, each contains a pair of integer keys U and V. All the keys are in the range of int

.

Output Specification:

For each given pair of U and V, print in a line LCA of U and V is A. if the LCA is found and A is the key. But if A is one of U and V, print X is an ancestor of Y. where X is A and Y is the other node. If U or V is not found in the binary tree, print in a line ERROR: U is not found.

or ERROR: V is not found. or ERROR: U and V are not found..

Sample Input:

6 8
7 2 3 4 6 5 1 8
5 3 7 2 6 4 8 1
2 6
8 1
7 9
12 -3
0 8
99 99

Sample Output:

LCA of 2 and 6 is 3.
8 is an ancestor of 1.
ERROR: 9 is not found.
ERROR: 12 and -3 are not found.
ERROR: 0 is not found.
ERROR: 99 and 99 are not found.

 

 

#include<bits/stdc++.h>
using namespace std;

#define bug(x) printf("x:%d****\n",x)

typedef long long LL;

#define rep(i,a,b) for(int i=a;i<b;++i)

const int N=20010;

map<int,int> id;

vector<int>vec[N];
int lmr[N],mlr[N];

int n,m;
void build(int* mlr,int* lmr,int len,int f){

	//printf("f:%d len:%d\n",f,len);
	if(len==0)return;
	int x=mlr[0];
    vec[f].push_back(x);vec[x].push_back(f);
	//if(len==1)return;

	int i;
	for(i=0;i<len;i++){
		if(lmr[i]==x)break;
	}
	build(mlr+1,lmr,i,x);
	build(mlr+i+1,lmr+i+1,len-i-1,x);
}


int seq[N*2],R[N*2],in[N],dep[N*2],tot=0;
void dfs(int x,int f){
	//printf("x:%d f:%d\n",x,f);
	seq[++tot]=x;
	R[tot]=dep[x];
	in[x]=tot;
	int sz=vec[x].size();
	for(int i=0;i<sz;i++){
		int v=vec[x][i];
		if(v==f)continue;
		dep[v]=dep[x]+1;
		dfs(v,x);
		seq[++tot]=x;
		R[tot]=dep[x];
	}
}

int Log[N],dp[N][20];
void RMQ(int n){
	int lm=Log[n];
	for(int i=0;i<=n;i++)dp[i][0]=i;
	for(int j=1;j<=Log[n];j++){
		for(int i=0;i+(1<<j)-1<=n;i++){
			int x1=dp[i][j-1],x2=dp[i+(1<<(j-1))][j-1];
			dp[i][j]=R[x1]<R[x2]?x1:x2;
		}
	}
}

int LCA(int x,int y){
	x=in[x],y=in[y];
	if(x>y)swap(x,y);

	int len=y-x+1;

	int t=Log[len];
	int u=dp[x][t],v=dp[y-(1<<t)+1][t];
	return R[u]<R[v]?seq[u]:seq[v];
}

int num[N];

int main()
{
    Log[1]=0;for(int i=2;i<N;i++)Log[i]=Log[i/2]+1;
    //rep(i,1,20)printf("i:%d %d\n",i,Log[i]);

    scanf("%d %d",&m,&n);
    for(int i=1;i<=n;i++){
		scanf("%d",&lmr[i]);
		num[i]=lmr[i];
    }
    for(int i=1;i<=n;i++){
		scanf("%d",&mlr[i]);
    }

    sort(num+1,num+n+1);
    rep(i,1,n+1)id[num[i]]=i;

	rep(i,1,n+1){
		lmr[i]=id[lmr[i]];
		mlr[i]=id[mlr[i]];
	}

    build(mlr+1,lmr+1,n,0);
	//bug(1);

	dfs(mlr[1],-1);
	RMQ(tot);

    rep(i,0,m){
		int u,v;
		scanf("%d %d",&u,&v);
		int f1=0,f2=0;
		if(id[u])f1=1;
		if(id[v])f2=1;
		if(!f1&&!f2){
			printf("ERROR: %d and %d are not found.\n",u,v);
		}else if(!f1||!f2){
			printf("ERROR: %d is not found.\n",f1?v:u);
		}else{
			int uu=id[u],vv=id[v];
			int x=LCA(uu,vv);
			//bug(x);bug(uu);bug(vv);
			if(x==uu||x==vv)
				printf("%d is an ancestor of %d.\n",num[x],num[uu+vv-x]);
			else
				printf("LCA of %d and %d is %d.\n",u,v,num[x]);
		}
    }
    return 0;
}
#include<bits/stdc++.h>
using namespace std;

#define bug(x) printf("x:%d****\n",x)

typedef long long LL;

#define rep(i,a,b) for(int i=a;i<b;++i)

const int N=20010;

map<int,int> id;

vector<int>vec[N];
int lmr[N],mlr[N];

int n,m;
void build(int* mlr,int* lmr,int len,int f){

	//printf("f:%d len:%d\n",f,len);
	if(len==0)return;
	int x=mlr[0];
    vec[f].push_back(x);vec[x].push_back(f);
	//if(len==1)return;

	int i;
	for(i=0;i<len;i++){
		if(lmr[i]==x)break;
	}
	build(mlr+1,lmr,i,x);
	build(mlr+i+1,lmr+i+1,len-i-1,x);
}


int dp[N][20],depth[N];

void dfs(int x,int fa){
	//printf("x:%d fa:%d\n",x,fa);
	dp[x][0]=fa;
	int sz=vec[x].size();
	for(int i=0;i<sz;i++){
		int v=vec[x][i];
		if(v==fa)continue;
		depth[v]=depth[x]+1;
		dfs(v,x);
	}
}
int max_h=0;


void RMQ(int n){
	while((1<<max_h)<n)max_h++;

	for(int j=1;j<=max_h;j++){
		for(int i=1;i<=n;i++){
			dp[i][j]=dp[dp[i][j-1]][j-1];
		}
	}
}

int LCA(int x,int y){
	if(depth[x]>depth[y])swap(x,y);
	int t=depth[y]-depth[x];
	for(int i=0;i<=max_h;i++){
		if(t&(1<<i)){
			y=dp[y][i];
		}
	}
	if(y==x)return x;
	//printf("x:%d y:%d\n",x,y);
	for(int i=max_h;i>=0;i--){
		if(dp[x][i]!=dp[y][i]){
			x=dp[x][i];
			y=dp[y][i];
		}
	}
	//printf("xx:%d yy:%d\n",x,y);
	return dp[x][0];
}



int num[N];
int main()
{
    //Log[1]=0;for(int i=2;i<N;i++)Log[i]=Log[i/2]+1;
    //rep(i,1,20)printf("i:%d %d\n",i,Log[i]);

    scanf("%d %d",&m,&n);
    for(int i=1;i<=n;i++){
		scanf("%d",&lmr[i]);
		num[i]=lmr[i];
    }
    for(int i=1;i<=n;i++){
		scanf("%d",&mlr[i]);
    }
    sort(num+1,num+n+1);
    rep(i,1,n+1)id[num[i]]=i;

	rep(i,1,n+1){
		lmr[i]=id[lmr[i]];
		mlr[i]=id[mlr[i]];
	}

	//bug(1);
    build(mlr+1,lmr+1,n,0);
   // bug(2);
	dfs(mlr[1],0);
	//bug(3);
	RMQ(n);

    rep(i,0,m){
		int u,v;
		scanf("%d %d",&u,&v);
		int f1=0,f2=0;
		if(id[u])f1=1;
		if(id[v])f2=1;
		if(!f1&&!f2){
			printf("ERROR: %d and %d are not found.\n",u,v);
		}else if(!f1||!f2){
			printf("ERROR: %d is not found.\n",f1?v:u);
		}else{
			int uu=id[u],vv=id[v];
			int x=LCA(uu,vv);
			//bug(x);bug(uu);bug(vv);
			if(x==uu||x==vv)
				printf("%d is an ancestor of %d.\n",num[x],num[uu+vv-x]);
			else
				printf("LCA of %d and %d is %d.\n",u,v,num[x]);
		}
    }
    return 0;
}

最近公共祖先有兩種表達方式

1. RMQ方式來實現。

     考慮樹的尤拉序,也就是dfs序,兩個點的最近公共祖先肯定在這中間,所以我們只需要知道在這兩個點中間的區間內

深度值最小的是誰,就好了。

       主要步驟:

                      (1)建圖,也就是找到所有的鄰接邊。

                       (2)dfs   (祖先結點一般是0)找到所有結點的父親結點

                         (3) 構造ST表

                       (4)查詢ST表。  注意要  in[a]<in[b]

2.樹形dp

       核心還是一種遞推關係,通過dfs我們可以直接找到他們的父親是誰,然後利用這種傳遞性,我們可以知道他的\LARGE 2^{j}的祖先是誰。 當詢問兩個點的最近公共祖先的時候,我們先跳到同一水平線上,然後看看是不是 \LARGE 2^{j}是同一個祖先,如果是的話,就不動,如果不是的話,我們就動,所以肯定可以到 最近公共祖先的子節點上,因為這個距離肯定可以用二進位制表達出來,所以我們就得到了結果。

            主要步驟:

                       (1)建圖,找到所有的鄰接邊

                        (2)dfs   找到所有結點的父節點,深度值

                        (3) 構造表

                         (4)查詢表。

 

樹的遍歷方式的轉換:

MLR  和  LMQ  構造出來樹。

考慮 MLR肯定能找到父節點, 所以肯定能在第一個序列中區分出來左右子樹。 但是在往下遞迴,就不是很好搞了,因為我們沒有辦法 確定一棵樹。  但是我們知道一棵樹上的結點肯定是相鄰的,所以我們只要知道 這棵樹的長度和起始位置就可以。

 

一開始肯定是一棵樹, 所以起始位置和長度是 很容易的, 然後在LMR中找到根,我們就可以知道左子樹的長度了,同理我們就又可以找到 MLR中左子樹的長度。  所以就又可以遞迴,直到結束。

所以我們發現 MLR提供了根節點, 而LMR 提供了子樹的長度,所以結合起來,我們就可以知道樹的範圍了。