用“倍增法”求最近公共祖先(LCA)


1.最近公共祖先:对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v
的祖先且x的深度尽可能大。
2.朴素算法:记录下每个节点的父亲,使节点u,v一步一步地向上找父亲,直到找到相同的“祖先”,即
是所求的答案,时间复杂度O(n)。
3.优化算法(倍增法):利用二进制的思想,想办法使一步一步向上搜变成以2^k地向上跳。
所以定义一个P[][]数组,使p[i][j]表示节点i的2^j倍祖先,因此p[i][0]即为i的父亲。
我们可以得到一个递推式p[i][j]=p[i][j-1][j-1]。这样子一个O(NlogN)的预处理(dfs)的 2^k 的祖先。
定义一个deep[]数组表示节点深度,
先判断是否 deep[u > deep[v]果是的话就交换一下(保证 u的深度小于 v方便下面的操作)然后把u到与
v同深度,同深度以后再把u v同时往上调(dec(j)) 调到有一个最小的j 满足: p[u] [j]!=p[v][j],u,v
是在不断更新的   最后把u,v 往上调 (u=p[u,0] v=p [v,0]) 一个一个向上调直到   u= v 这时 u or v
就是公共祖先。复杂度:O(logn)

 1 #include<bits/stdc++.h>

 2 using namespace std;  3 int N,root;  4 struct node{  5     int rc,lc,fa,deep;  6 };  7 node tree[2000];  8 int p[2000][2000];  9 int a,b; 10 void dfs(int r); 11 int LCA(int,int); 12 int main(){ 13     cin>>N; 14     for(int i=1;i<=N-1;i++){ 15         int f,c; 16         cin>>f>>c; 17         if(tree[f].lc!=0) tree[f].rc=c; 18         else tree[f].lc=c; 19         tree[c].fa=f; 20         p[c][0]=f;//c的2^0==1,即它的一倍祖先是它父亲 

21  } 22     for(int i=1;i<=N;i++){ 23         if(tree[i].fa==0){ 24             root=i; 25             break; 26  } 27  } 28     tree[root].deep=1; 29  dfs(root); 30     cin>>a>>b; 31     cout<<LCA(a,b); 32     

33     return 0; 34 } 35 void dfs(int r){ 36     if(r==0) return ; 37     int ll=tree[r].lc; 38     int rr=tree[r].rc; 39     tree[ll].deep=tree[r].deep+1; 40     tree[rr].deep=tree[r].deep+1; 41     for(int i=1;i<=N;i++){//处理P数组 

42         int zu=1; 43         for(int k=1;k<=i;k++) zu*=2; 44         if(zu<=tree[r].deep) 45         p[r][i]=p[p[r][i-1]][i-1]; 46         else break; 47  } 48  dfs(ll); 49  dfs(rr); 50 } 51 int LCA(int x,int y){ 52     if(tree[x].deep<tree[y].deep) swap(x,y);//保证x比y高

53     int delta=tree[x].deep-tree[y].deep; 54     //使x和y移到统一高度 

55     for(int i=0;i<=30;i++){ 56         if((1<<i)&delta){//把跳的路径拆成2^k... 

57             x=p[x][i]; 58  } 59  } 60     

61     if(x==y) return x; 62     for(int i=N-1;i>=0;i--){ 63         if(p[x][i]!=p[y][i]){//保证不一样才跳,防止跳出最近祖先 

64             x=p[x][i]; 65             y=p[y][i]; 66  } 67  } 68     

69     return p[x][0];//因为不断跳到不一样的节点,所以ans一定是x或y的2^0倍祖先 

70 }

 

你可能感兴趣的:(ca)