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 }
时间: 2024-08-10 23:21:13