1. 程式人生 > 其它 >淺談倍增LCA

淺談倍增LCA

倍增LCA

LCA:已知一個樹和書上兩個點,求兩個點的最近公共祖先。

倍增:將2的所有次冪排成一個序列,相加可以得到所有正整數,這樣就可以在找最近公共祖先時不是一層一層找而是每次找2的冪次方層,大大提高演算法效率。

演算法思路

1,用鏈式前向星存圖,找到根節點並從根節點開始進行深度優先搜尋,儲存每個節點的深度;

2,對要查詢的兩個節點,先將深度較深的結點向上查詢直到其深度與深度較淺的結點一致;

3,兩個節點同步向上挑2的冪次方層,直到找到公共祖先。

大板子:https://www.luogu.com.cn/problem/P3379

今天心情好註釋比較多。

 1 #include<bits/stdc++.h>
 2
#define ff(i,s,e) for(int i=s;i<=e;i++) 3 #define fff(i,s,e) for(int i=s;i>=e;i--) 4 using namespace std; 5 inline int read(){ 6 register int x=0,f=1; 7 register char ch=getchar(); 8 while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='
9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} 10 return x*f; 11 } 12 const int N=5e5+2; 13 int n,m,s,cnt,head[N]; 14 int dep[N],f[N][21]; 15 struct qwq{ 16 int v,nxt; 17 }e[N<<1];//無向圖開雙倍 18 void add(int u,int v){//鏈式前向星儲存 19 e[++cnt].v=v; 20 e[cnt].nxt=head[u]; 21 head[u]=cnt;
22 } 23 void dfs(int x,int fa){//深搜存每個節點深度 24 dep[x]=dep[fa]+1;//孩子是爹的深度+1 25 f[x][0]=fa;//更新當前x的爸爸(即2^0=1代祖宗 26 for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];//x的2^i代祖宗是x的2^i-1代祖宗的2^i-1代祖宗 27 for(int i=head[x];i;i=e[i].nxt) if(e[i].v!=fa) dfs(e[i].v,x);//遍歷x連線的所有點為他們找爸爸 28 } 29 int lca(int x,int y){ 30 if(dep[x]<dep[y]) swap(x,y);//令x為深度較深的結點 31 fff(i,20,0) if(dep[f[x][i]]>=dep[y]) x=f[x][i];//若x的2^i級爸爸的深度大於等於y的深度,更新x 32 if(x==y) return x;//特判,x的爸爸就是y 33 fff(i,20,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];//若x和y的爸爸不相同,同時往上跳直到找到x和y的共同爸爸 34 return f[x][0];//返回x的爸爸 35 } 36 int main(){ 37 n=read(),m=read(),s=read(); 38 int x,y; 39 ff(i,1,n-1){ 40 x=read(),y=read(); 41 add(x,y); 42 add(y,x); 43 } 44 dfs(s,0); 45 int a,b; 46 ff(i,1,m){ 47 a=read(),b=read(); 48 printf("%d\n",lca(a,b)); 49 } 50 return 0; 51 }