淺談多種方法求LCA(最近公共祖先)
阿新 • • 發佈:2020-12-11
首先來看下例題
洛谷 P3379 https://www.luogu.com.cn/problem/P3379
相信求LCA是每位Oier學習演算法的必經之路
那麼啥是LCA呢?
引用OI wiki的定義——
那麼步入正題 如何求LCA呢?
第一種方法·樸素求法
我們選擇深度最深的點往上跳 最後兩點一定會相遇 相遇的位置即是我們要的LCA
dfs整棵樹是O(n)的,單次查詢複雜度為O(n),但實際是log(n)的(隨機樹高)
第二種方法·倍增求LCA
code如下
#include<bits/stdc++.h> using namespace std; const int maxn=500010; int head[maxn],cnt=0,fa[maxn][22],Log[maxn],dep[maxn],n,m,s; struct edge { int next,to; }e[maxn<<1]; inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline void pre() { Log[1]=0; Log[2]=1; for(register int i=3;i<=maxn;++i) { Log[i]=Log[i/2]+1; } } inline void dfs(int now,int father) { fa[now][0]=father; dep[now]=dep[father]+1; for(register int i=1;i<=Log[dep[now]];++i) { fa[now][i]=fa[fa[now][i-1]][i-1]; } for(int i=head[now];i;i=e[i].next) { if(e[i].to!=father) dfs(e[i].to,now); } } inlineint lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); while(dep[x]>dep[y]) { x=fa[x][Log[dep[x]-dep[y]]-1]; } if(x==y) return x; for(register int k=Log[dep[x]];k>=0;--k) { if(fa[x][k]!=fa[y][k]) { x=fa[x][k]; y=fa[y][k]; } } return fa[x][0]; } int main() { scanf("%d%d%d",&n,&m,&s); for(register int i=1;i<=n-1;++i) { int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } for(register int i=1;i<=n;++i) { Log[i]=Log[i-1]+(1<<Log[i-1]==i); } dfs(s,0); for(register int i=1;i<=m;++i) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }
第三種方法·Tarjan
待更新
第四種方法·樹鏈剖分
待更新
第五種方法·RMQ求LCA
講解待更新
code如下:
#include<bits/stdc++.h> using namespace std; int n,m,s,cnt=0,tot=0; const int maxn=1000010; int head[maxn],f[maxn][25],Log[maxn],rec[maxn][25]; int fir[maxn],ver[maxn],r[maxn];//fir[i] is the first position of i //ver[i]RMQ序;r[i]是ver[i]所處深度 struct edge { int next,to; }e[maxn<<1]; inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline int read() { char ch; int res,sign=1; while((ch=getchar())<'0'||ch>'9') if(ch=='-') sign=-1; res=ch^48; while((ch=getchar())>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^48); return res*sign; } inline void dfs(int u,int dep)//dfs處理出三個陣列 { fir[u]=++tot,ver[tot]=u,r[tot]=dep; for(int i=head[u];i!=-1;i=e[i].next) { int v=e[i].to; if(!fir[v]) { dfs(v,dep+1); ver[++tot]=u,r[tot]=dep; } } } inline void pre() { Log[1]=0; Log[2]=1; for(register int i=3;i<=maxn;++i) { Log[i]=Log[i/2]+1; } } int main() {{ memset(head,-1,sizeof(head)); n=read(),m=read(),s=read(); for(int i=1;i<n;i++) { int x,y; x=read(),y=read(); add(x,y); add(y,x); } dfs(s,1); //pre(); for(register int i=1;i<=tot;++i) { f[i][0]=r[i],rec[i][0]=ver[i]; } for(register int j=1;j<=log(tot)/log(2);++j) for(register int i=1;i+(1<<j)-1<=tot;++i) { if(f[i][j-1]<f[i+(1<<(j-1))][j-1]) { f[i][j]=f[i][j-1],rec[i][j]=rec[i][j-1]; } else { f[i][j]=f[i+(1<<(j-1))][j-1],rec[i][j]=rec[i+(1<<(j-1))][j-1]; } } for(int i=1;i<=m;i++) { int l,r; l=read(),r=read(); l=fir[l],r=fir[r]; if(l>r) swap(l,r); int k=0; while((1<<k)<=r-l+1) k++; k--; if(f[l][k]<f[r-(1<<k)+1][k]) printf("%d\n",rec[l][k]); else printf("%d\n",rec[r-(1<<k)+1][k]);//常見的ST表輸出 } return 0; }}
以上就是全部內容!!!後續會更新一些題目和演算法~