lca
Posted liuwenyao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了lca相关的知识,希望对你有一定的参考价值。
http://www.cnblogs.com/zhouzhendong/p/7256007.html
LCA 最近公共祖先,只在同一个树中距离两个子节点最近的那个父节点。
在网上最快的有两种算法分别是算法发明家——Tarjan 以他名字发明的算法,和倍增算法,和树剖。
而Tarjan算法几乎只能求两个点之间的最近公共祖先,因此我们不介绍,而倍增算法便是通过倍增不让每一个子节点一步步地向上找,而是跳着找。
这样肯定比一般的暴力寻找快,而应该怎么实现呢,我们应用一个邻接表存图,用来处理每条边的边权用来应对某些题。
对于这个算法,我们从最暴力的算法开始:
①如果aa和bb深度不同,先把深度调浅,使他变得和浅的那个一样
②现在已经保证了aa和bb的深度一样,所以我们只要把两个一起一步一步往上移动,直到他们到达同一个节点,也就是他们的最近公共祖先了。
而实际上,一步一步往上移动太慢,我们可以做一个预处理:
fai,jfai,j表示节点ii往上走2j2j次所到达的祖先,那么不难写出转移式:
fai,0=fatheri,fai,j=fafai,j−1,j−1fai,0=fatheri,fai,j=fafai,j−1,j−1
然后在求LCALCA的时候,有这样一个性质:(假设aa和bb深度一样)
设anstx,yanstx,y为节点x网上走y步到达的祖先,对于一个kk,如果ansta,k=anstb,kansta,k=anstb,k,那么对于所有k′(k′>k)k′(k′>k),一定有ansta,k′=anstb,k′ansta,k′=anstb,k′;对于一个kk,如果ansta,k≠anstb,kansta,k≠anstb,k,那么对于所有k′(k′<k)k′(k′<k),一定有ansta,k′≠anstb,k′ansta,k′≠anstb,k′,而且LCA(a,b)=LCA(ansta,k,anstb,k)LCA(a,b)=LCA(ansta,k,anstb,k)。
于是求法就渐渐的现行了:
1. 把aa和bb移到同一深度(设depthxdepthx为节点xx的深度),假设deptha≤depthbdeptha≤depthb,所以我们的目的是把bb向上移动i=(depthb−deptha)i=(depthb−deptha)层,那么,由于之前有预处理的fafa数组,我们把ii写成二进制形势,然后利用fafa数组来在O(logn)O(log?n)的复杂度中完成;
2. 寻找aa和bb的LCALCA下一层的两个祖先。利用之前的那个性质,再利用倍增,如果aa和bb的第2k2k个祖先不是同一个,那么把aa改为faa,kfaa,k,bb改为fab,kfab,k,kk减11;否则直接kk减11;当然在这之前要实现确定kk的最大值,从大往小处理下去。最终的结果就是faa,0faa,0或者fab,0fab,0。
注意,如果aa和bb在调节深度之后已经是同一个祖先的,那么直接返回aa或者bb。
洛谷LCA模板代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxlog=20; const int maxn=550000; int n,m,s; int root; int fa[maxn][maxlog]; int deep[maxn]; int lin[1000010],tot; struct cym{ int next,to; }e[2*maxn]; void add(int u,int v) { e[++tot].to=v; e[tot].next=lin[u]; lin[u]=tot; } void dfs(int u,int father,int d) { fa[u][0]=father; deep[u]=d; for(int i=lin[u];i!=-1;i=e[i].next) if(e[i].to!=father) dfs(e[i].to,u,d+1); } void init() { dfs(root,-1,0); for(int k=0;k+1<maxlog;k++) { for(int v=1;v<=n;v++) if(fa[v][k]<0) fa[v][k+1]=-1; else fa[v][k+1]=fa[fa[v][k]][k]; } } int lca(int u,int v) { if(deep[u]>deep[v])//如果u在上面,交换 swap(u,v); for(int k=0;k<maxlog;k++) { if((deep[v]-deep[u])>>k&1) v=fa[v][k]; } if(u==v) return u; for(int k=maxlog-1;k>=0;k--) { if(fa[v][k]!=fa[u][k]) { u=fa[u][k]; v=fa[v][k]; } } return fa[u][0]; } int main() { memset(lin,-1,sizeof(lin)); int a,b; scanf("%d%d%d",&n,&m,&root); for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); add(a,b); add(b,a); } init(); for(int i=1;i<=m;i++) { int u,v,a; scanf("%d%d",&u,&v); a=lca(u,v); printf("%d ",a); } return 0; }
以上是关于lca的主要内容,如果未能解决你的问题,请参考以下文章
代码源 Div1 - 105#451. Dis(倍增求LCA)