LCA(最近公共祖先)暴力转倍增算法
今天,南外夏令营第一天打卡 , 咳咳,
LCA(最近公共祖先)
概念:为了便于了解,如图(在一颗有根树中,有若干个子树):
其中1号节点为该树的根,我们所谓的祖先即一个子树的根及其的根的根和其的根的根的根……(在LCA中包括子树本身)(其实不用那么绕口),举个例子(在本文之后直接用编号来称呼节点):
5的祖先:5、2、1;7的祖先:7、3、1;2的祖先:2、1……
而所谓的最近公共祖先为两个节点首先能找到的公共祖先(即深度最最大的公共祖先)
来个模版题()
下面就是激动人心的代码时刻让我们进行代码实现的步骤:
1、先从最简单的暴搜入手:通过链表(从根一直往下搜,标记父节点)
大概框架(时间复杂度nlogn)
struct st{ int to,next; }edge[]; int hd[]; void add(int from,int to) { cnt++; edge[cnt].next=hd[from]; edge[cnt].to=to; hd[from]=cnt; } void DFS(int now,int d)找祖先 { deep[now]=d; for (int i=hd[now];i;i=edge[i].next) { fa[edge[i].to]=now; DFS(edge[i].to,d+1); } } void LCA() { if (deep[x]<deep[y])swap(x,y); while (deep[x]>deep[y])x=fa[x]; while (x!=y) { x=fa[x]; y=fa[y]; } ans=x;//或ans=y }
2、倍增算法(时间复杂度logn)
首先,我们需要一定的二进制的基础:
结论1:二进制的所有位只存在0或1,
结论2:把十进制转二进制:10(10)-->1010(2),所以我们发现:一个十进制非负整数可以一个二进制数,(即可理解为一些2的次方相加!(这些次方一定不同,因为结论1得))
结论3:2j-1+2j-1=2j!
运用到这里来:设f[i][j]:i号节点的第2j代的祖先编号,因为2j-1+2j-1=2j,所以一个节点的2j代的祖先是其2j-1代的祖先的2j-1的祖先,得f[i][j]=f[f[i][j-1]][j-1]
把暴力转倍增:
1、把x,y转同深度(因为deep[x]的ans代祖先==deep[y]),ans转成一些2的次方相加:
for (int i=maxn;i>=0;i--)//必须从小到大:因为5=22+20,而如果从小到大:20+21=3,但3+22>5,不能"悔棋"!!! { if (deep[f[x][i]]>=deep[y])x=f[x][i]; }
2、找共同祖先:
这里和上面的差不多:当前两个节点的第N代祖先是共同祖先,那他们的第N+x(x为非负整数)代祖先一定共同祖先
为了防止越过N代,所以我们求第N-1代是什么,再求其的父辈!
for (int i=maxn;i>=0;i--)//必须从小到大,同理 { if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0];
这就大功告成!!
struct st{ int to,next; }edge[]; int hd[],cnt,deep[],f[][],ans,n,m,s; int Find(int t) { int u=1; while ((u<<1)<=t)u<<=1; } void add(int from,int to) { cnt++; edge[cnt].next=hd[from]; edge[cnt].to=to; hd[from]=cnt; } void DFS(int now,int d) { deep[now]=d; int maxn=Find(d); for (int i=1;i<=maxn;i++) { f[now][i]=f[f[now][i-1]][i-1]; } for (int i=hd[now];i;i=edge[i].next) { f[edge[i].to][0]=now; DFS(edge[i].to,d+1); } } int LCA(int x,int y) { if (deep[x]<deep[y])swap(x,y); int maxn=Find(x); for (int i=maxn;i>=0;i--)//必须从小到大 { if (deep[f[x][i]]>=deep[y])x=f[x][i]; } if (x==y)return x; maxn=Find(y); for (int i=maxn;i>=0;i--)//必须从小到大 { if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; }//仅供参考