LCA(Least Common Ancestors),即最近公共祖先,是指在有根树中,找出某两个结点u和v最近的公共祖先。 (来自百度百科)
一.倍增求LCA
预处理出距点u距离为2^0,2^1,2^2...的点作为f[u][0],f[u][1],f[u][2]...
d[u]记录点u的深度
用二进制拆分的思想,让更深的点向上跳,直至与另一个点深度相同
此时,如果两个点重合了,那么那个浅的点本身就是最近公共祖先
若两个点没有重合,则继续按二进制拆分的思想,两个点同时跳相同高度,跳过了就跳短一些,直至两个点重合
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #define R register int 5 using namespace std; 6 const int N=500010; 7 inline int g() { 8 R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; 9 do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; 10 } 11 struct node{ 12 int v,nxt; 13 #define v(i) e[i].v 14 #define nxt(i) e[i].nxt 15 }e[N<<1]; 16 int n,m,s,cnt; 17 int fir[N],d[N],f[N][20]; 18 inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;} 19 inline void dfs(int u) { 20 for(R i=fir[u];i;i=nxt(i)) { 21 R v=v(i); 22 if(d[v]) continue; 23 d[v]=d[u]+1; f[v][0]=u; R p=u; 24 for(R j=0;f[p][j];++j) f[v][j+1]=f[p][j],p=f[p][j]; 25 dfs(v); 26 } 27 } 28 inline int ask(int u,int v) { 29 if(d[v]>d[u]) swap(u,v); 30 R lim=log2(d[u])+1; 31 for(R i=lim;i>=0;--i) if(d[f[u][i]]>=d[v]) u=f[u][i]; 32 if(u==v) return u; 33 for(R i=lim;i>=0;--i) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; 34 return f[u][0]; 35 } 36 signed main() { 37 n=g(),m=g(),s=g();//n为结点数,m为询问次数,s为根节点 38 for(R i=1,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u); 39 d[s]=1; dfs(s); 40 for(R i=1;i<=m;++i) { 41 R u=g(),v=g(); 42 printf("%d ",ask(u,v)); 43 } 44 }
二.tarjan求LCA
Tarjan是的离线的
从根结点开始dfs,对遍历到的结点u标记已访问,创建新集合,元素为u,再遍历u的每一个子节点v,回溯时将每个子节点v的集合并到u的集合上,用并查集记录集合中的每个元素的fa为u,接着处理询问,对于关于u的每一个询问,若另一个结点v已访问,则可断定LCA(u,v)为fa[v],记录结果,最后按顺序输出即可
1 #include<cstdio> 2 #include<iostream> 3 #define R register int 4 const int N=500010; 5 using namespace std; 6 inline int g() { 7 R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix; 8 do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix; 9 } 10 struct node{ 11 int v,nxt; 12 #define v(i) e[i].v 13 #define nxt(i) e[i].nxt 14 #define vv(i) q[i].v 15 #define nn(i) q[i].nxt 16 }e[N<<1],q[N<<1]; 17 int n,m,s,cnt,cntq; 18 int fir[N],fq[N],lca[N],fa[N]; 19 bool vis[N],vq[N]; 20 inline void add(int u,int v) {v(++cnt)=v,nxt(cnt)=fir[u],fir[u]=cnt;} 21 inline void addq(int u,int v) {vv(++cntq)=v,nn(cntq)=fq[u],fq[u]=cntq;} 22 inline int getf(int x) {return x==fa[x]?x:fa[x]=getf(fa[x]);} 23 inline void tarjan(int u) { 24 vis[u]=true; 25 for(R i=fir[u];i;i=nxt(i)) { 26 R v=v(i); 27 if(vis[v]) continue; 28 tarjan(v); 29 fa[getf(v)]=u; 30 } 31 for(R i=fq[u];i;i=nn(i)) { 32 R v=vv(i); 33 if(vis[v]&&!vq[(i+1)>>1]) lca[(i+1)>>1]=getf(v),vq[(i+1)>>1]=true; 34 } 35 } 36 signed main(){ 37 n=g(),m=g(),s=g(); 38 for(R i=1;i<=n;++i) fa[i]=i; 39 for(R i=1,u,v;i<n;++i) u=g(),v=g(),add(u,v),add(v,u); 40 for(R i=1,u,v;i<=m;++i) u=g(),v=g(),addq(u,v),addq(v,u); 41 tarjan(s); 42 for(R i=1;i<=m;++i) printf("%d ",lca[i]); 43 }
2019.04.02