最近公共祖先(LCA) 倍增法

  1. 最近公共祖先,顾名思义,就是在一颗树中,去找两个子节点的最近的公共祖先.这处理起来并不复杂,我们可以直接一个一个的暴力去 找,但基本上会TLE.所以我们可以用倍增法来处理LCA的问题.

  2. 倍增法:每次按\(2\)的倍数增大,即:\(1,2,4,8,16,32.....\),有点类似二进制的枚举,而在当前处理的LCA问题中,我们要从大的向小的来找.

  3. 具体实现:

    1. 既然我们是倍增法来处理的,那么我们就要求出每个节点的深度以及它们的前\(2^k\)个祖先.然后我们遍历每个点的深度的对数,即\(lg[depth[node]]-1\)(lg表示\(log_{2}{n}+1\)),求出\(fa[node][i]\),表示节点的\(2^i\)级祖先,而在求的时候要注意,我们更新的时候,要去找当前的这个点的上\(2^{i-1}\)级祖先的上\(2^{i-1}\)级祖先,即:\(2^{i-1}+2^{i-1}=2^i\).

    2. 代码:

      void dfs(int node,int fath){
      	fa[node][0]=fath;
      	depth[node]=depth[fath]+1;
      	for(int i=1;i<=lg[depth[node]]-1;++i){
      		fa[node][i]=fa[fa[node][i-1]][i-1];
      	}
      	for(auto w:V[node]){
      		if(w!=fath) dfs(w,node);
      	}
      }
      
    3. 预处理完之后,我们就可以来找两个点的LCA了,首先我们要保证\(x\)点是深度大的那个,然后再让它们处于同一深度,此时假如两个点相等,那么\(LCA(x,y)=x\),否则,我们就要用倍增法来查找:

    4. 首先去找最上面的点(\(2^k\)级祖先),如果\(fa[x][k]=fa[y][k]\),就说明当前这个点已经是它们的公共祖先了,为了求最近,我们再去找x和y的\(2^{k-1}\)级祖先,如果\(fa[x][k-1]!=fa[y][k-1]\),那么说明它们的LCA在其\(2^{k-1}\)级祖先之上,我们更新\(x=fa[x][k-1],y=fa[y][k-1]\),然后再向上去找.容易发现,这样一定能在有限的次数之内找到它们的LCA.

    5. 代码:

      int LCA(int x,int y){
      	if(depth[x]<depth[y]){
      		swap(x,y);
      	}
      	while(depth[x]>depth[y]){
      		x=fa[x][lg[depth[x]-depth[y]]-1];
      	}
      	if(x==y) return x;
      	for(int k=lg[depth[x]]-1;k>=0;--k){
      		if(fa[x][k]!=fa[y][k]){
      			x=fa[x][k];
      			y=fa[y][k];
      		}
      	}
      	return fa[x][0];
      }
      

猜你喜欢

转载自www.cnblogs.com/lr599909928/p/13364950.html