[筆記]倍增求LCA
阿新 • • 發佈:2020-08-22
[筆記]倍增求LCA
原題鏈
演算法描述
LCA是指兩個點的最近公共祖先.
在程式中設f[x][k]
表示x的2k*輩祖先,即從x向上(根節點)走*2k步所到達的節點,如果向上走到的節點不存在則令f[x][k] = 0
,並且f[x][0]
為x節點的父節點,因為2^0 = 1,且任意一個節點向上走一步到達的就是它的父節點.同時我們會發現一個性質:∀k∈[1,log(n)],f[x][k] = f[f[x][k - 1][k - 1]]
,這個式子的意思是x向上跳2k*步所到達的節點與x向上跳*2(k-1)步所到達的節點再向上跳*2(k-1)*步所到達的節點是相同的,可以用**同底數冪相加**的原理證明~~(a
如何求LCA:設dis[x]為x節點的深度,並規定dis[x] ≥ dis[y]如果不滿足這個條件則交換x,y;此時我們保證了x的深度一定大於y,所以我們先嚐試將x向上跳2(logn)步,…跳21步,跳2^0步,並檢查所跳到的節點是否仍比y深,如果仍比y深則繼續上跳.如果調到x==y
則說明x,y已經到達了兩者的LCA處,直接輸出.但如果當兩者的深度一致時x,y仍不相等,此時將x,y同時上跳直到f[x][i] == f[y][i]
,則說明此時x,y的父節點是同一個點,即為兩者的LCA.演算法結束
AC程式碼
#include <bits/stdc++.h> using namespace std; struct node{ int to,next; }edge[1000010]; int fir[1000010],n,m,s,tot,t,dis[500010],f[500010][35]; void add(int x,int y){//前向星存邊 tot++; edge[tot].to = y; edge[tot].next = fir[x]; fir[x] = tot; } void bfs(){//預處理出每個點的深度,並求出f陣列 memset(dis,0,sizeof(dis)); queue < int > q; while(!q.empty())q.pop(); dis[s] = 1; q.push(s); while(!q.empty()){ int x = q.front(); q.pop(); for(int i = fir[x];i;i = edge[i].next){ if(dis[edge[i].to] != 0)continue; f[edge[i].to][0] = x; dis[edge[i].to] = dis[x] + 1; for(int j = 1;j <= t;j++){ f[edge[i].to][j] = f[f[edge[i].to][j - 1]][j - 1]; } q.push(edge[i].to); } } return; } int lca(int x,int y){ if(dis[x] < dis[y])swap(x,y); if(x == y)return x; for(int i = t;i >= 0;i--){ if(dis[f[x][i]] >= dis[y])x = f[x][i]; } if(x == y)return x; for(int i = t;i >= 0;i--){ if(f[x][i] != f[y][i]){ x = f[x][i]; y = f[y][i]; } } return f[x][0]; } int main(){ scanf("%d%d%d",&n,&m,&s); for(int i = 1;i < n;i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } t = (int)(log(n) / log(2)) + 1; bfs(); for(int i = 1;i <= m;i++){ int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }