LCA
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LCA相关的知识,希望对你有一定的参考价值。
概念描述
LCA(Least Common Ancestors):即最近公共祖先,是指这样一个问题:在有根树中,找出某两个结点u和v的所有祖先中距离(u,v)最近的那个公共祖先(也就是离根最远的那个公共祖先)。
算法思想:主要有3种 1.倍增(在线) 2.tarjan(离线) 3.RMQ+ST
1.倍增
f[i][j]表示i向上跳2的j次方步是谁
则有f[i][j]=f[f[i][j-1]][j-1]
注意初始时f[root][i]=root(i-->1-20)
预处理一遍后
要找树上两点的LCA
首先要保证它们在同一深度
若不在,则用相同“跳”的方式先放在同一深度
然后在同一深度用跳的方式找到LCA即可
时间复杂度O(n*logn)
#include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #include<stack> #include<cstring> using namespace std; stack<int> s; int f[100001][21],g[100001],dep[100001],n,cnt,T; int root1; struct node{ int nxt,to; }e[100001]; bool root[100001]; inline void addedge(int x,int y){ e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y; } inline void dfs(int u){ dep[u]=1;s.push(u); while(!s.empty()){ u=s.top();s.pop(); if(u!=root1) for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1]; else for(int i=0;i<=20;i++) f[u][i]=u; for(int i=g[u];i;i=e[i].nxt){ if(!dep[e[i].to]){ dep[e[i].to]=dep[u]+1; f[e[i].to][0]=u; s.push(e[i].to); } } } } inline int swim(int x,int h){ for(int i=0;h;i++,h>>=1) if(h&1) x=f[x][i]; return x; } inline int lca(int x,int y){ if(dep[x]<dep[y]){ int t; t=x; x=y; y=t; } x=swim(x,dep[x]-dep[y]); if(x==y) return x; while(1){ int i; for(i=0;f[x][i]!=f[y][i];i++); if(!i) return f[x][0]; x=f[x][i-1];y=f[y][i-1]; } } inline void init(){ int u1,v1; memset(root,0,sizeof(root)); memset(g,0,sizeof(g)); memset(dep,0,sizeof(dep)); cnt=0; for(int i=1;i<=100000;i++) for(int j=0;j<=20;j++) f[i][j]=0; scanf("%d",&n); for(int i=1,u,v;i<=n-1;i++){ scanf("%d%d",&u,&v); addedge(u,v); root[v]=1; } scanf("%d%d",&u1,&v1); for(int i=1;i<=n;i++) if(root[i]==0) root1=i; f[root1][0]=root1; dfs(root1); printf("%d\\n",lca(u1,v1)); return; } int main(){ scanf("%d",&T); while(T--) init(); return 0; }
P.S.:不要吐槽为什么是dfs QAQ
2.tarjan离线
这里安利一下一篇讲的很好的博客
http://alanyume.com/130.html
详细的算法思想就全部在这里了
然后代码实现。。。
各种错误调了半天。。。终于答案正确了但是会多输出正确答案(应该和我没有用离线存然后针对这题强行在线输出有关),最后在孙大爷的建议下乱插flag插过了。。。
由于不会vector所以对怎么离线存储下来还是有点懵逼
#include<iostream> #include<cstdlib> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; int ind[100001],g[100001],fa[100001],root,cnt,u,v,n,T,flag; bool vis[100001]; struct node{ int nxt,to; }e[100001]; inline void addedge(int x,int y){ e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y; } inline int gf(int x){ if(fa[x]==x) return x; else return fa[x]=gf(fa[x]); } inline void marge(int x,int y){ int fx=gf(x),fy=gf(y); if(fx==fy) return; else fa[fy]=fx; } inline void tarjan(int x){ if(flag==1) return; vis[x]=1; for(int i=g[x];i;i=e[i].nxt){ tarjan(e[i].to); marge(x,e[i].to); } if(flag) return; if(x==u&&vis[v]==1){ printf("%d\\n",gf(v)); flag=1; return; } if(flag) return; if(x==v&&vis[u]==1){ printf("%d\\n",gf(u)); flag=1; return; } if(flag) return; } inline void clear(){ memset(fa,0,sizeof(fa)); memset(g,0,sizeof(g)); memset(vis,0,sizeof(vis)); memset(ind,0,sizeof(ind)); cnt=0;flag=0; for(int i=1;i<=n;i++) fa[i]=i; } inline void init(){ scanf("%d",&n); clear(); for(int i=1;i<=n-1;i++){ scanf("%d%d",&u,&v); addedge(u,v); ind[v]=1; } scanf("%d%d",&u,&v); for(int i=1;i<=n;i++) if(ind[i]==0){ root=i; break; } tarjan(root); } int main(){ scanf("%d",&T); while(T--){ init(); } return 0; }
以上是关于LCA的主要内容,如果未能解决你的问题,请参考以下文章
代码源 Div1 - 105#451. Dis(倍增求LCA)