[LCA]倍增法
Posted kaike
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[LCA]倍增法相关的知识,希望对你有一定的参考价值。
LCA(least common ancestors)最近公共祖先
指的就是对于一棵有根树,若结点z既是x的祖先,也是y的祖先(不要告诉我你不知道什么是祖先),那么z就是结点x和y的最近公共祖先。
定义到此。
那么怎么求LCA?
对于朴素思想,就是我要一步一步往上爬。。一步一步走。先把结点x和y整到同一深度,然后再一次一个深度的往上查,直到祖先一样才break(明显是个while)
但是,一步一步实在是太慢了,所以不能脚踏实地地走
那么,考虑跳着走,
跳着走的条件就是要满足一步步数尽可能多并且不跳过了。当然你跳过了在一步一步往下找也行啊QWQ
于是,在满足这两个条件的情况下,我们有了LCA算法:
一步条2的n次方。
对于每一步跳跃,我们要预处理一个二维数组f(father)f[x][i],表示对于当前的x结点,往上跳2^i个祖先,特别的,像数学中的,f[x][0]就是x的一代祖先。于是我们要用一个dfs预处理来解决这个f数组。后面我们会提到;
处理完f数组,那就很简单了。
输入x和y,表示要求结点x和结点y的LCA,那么我们开始求LCA:
对于x和y在不同高度,我们先把他们拉到一个高度,同样不能一步一步走,也要用到f数组。
这里我们要提前了解到一个定理:对于任意一个非零整数,我们都可以将他用2的次幂表示出来。也就是such as : 11=2^3+2^1+2^0。这倒也不用证明,就像每个数都可以用二进制表示一样。
接着讲:在把x和y弄到同一高度时,我们要先做的是设x的深度dep要比y的深度dep要大,如果dep[y]>dep[x],那么把他们交换。原因是如果不交换,那么我们需要判断两种情况,用两个if以及几乎相同的代码。。(乱七八糟还占内存)
然后用一个判断 if(dep[f[x][i]]>=dep[y])x=f[x][i];(此时x深度大于y),在这里用外层for循环来枚举i,但是i一定要从大到小!。
那么,类比于x和y的深度的距离,这个瓶子的容积也是同样道理。从2的尽可能大的次幂去找,一旦能找到能接近他们的i,就更新dep[x],直到相等。类似于无限逼近,最后值相等的过程。如果i相等了,就说明他们的深度已经在同一层了。
与倍增到同一深度的过程相比,倍增找公共祖先也是类似的,但是有一点不同,就是你不知道什么时候找到他们的公共祖先,因此就没有查找的上限,那么就让他们尽可能接近。因此条件改成了这个:
if(f[x][i]!=f[y][i])
{
x=f[x][i];y=f[y][i];
}
在执行完这个语句后,我们成功让x和y变成了某一节点z的左右儿子!
因为左右儿子是最接近但是又不相等的。
那么我们随便取其中一个找爸爸,就找到了LCA。
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 #include <algorithm> 5 #include <set> 6 #include <queue> 7 #include <stack> 8 #include <string> 9 #include <cstring> 10 #include <vector> 11 #include <map> 12 //#include <unordered_map> 13 #define mem( a ,x ) memset( a , x ,sizeof(a) ) 14 #define rep( i ,x ,y ) for( int i = x ; i<=y ;i++ ) 15 #define lson l ,mid ,pos<<1 16 #define rson mid+1 ,r ,pos<<1|1 17 using namespace std; 18 typedef long long ll ; 19 typedef pair<int ,int> pii; 20 typedef pair<ll ,int> pli; 21 const int inf = 0x3f3f3f3f; 22 const ll mod=998244353; 23 const int N=1e5+50; 24 int n,xx,yy,m; 25 int Link[5010],len=0,val[5010]; 26 int deep[5010]; 27 struct node 28 { 29 int y,next,id; 30 }e[N]; 31 void insert(int xx,int yy,int id) 32 { 33 e[++len].next=Link[xx]; 34 Link[xx]=len; 35 e[len].y=yy; 36 e[len].id=id; 37 } 38 void dfs(int x,int fa) 39 { 40 deep[x]=deep[fa]+1; 41 for(int i=1;(1<<i)<=deep[x];i++) 42 f[x][i]=f[f[x][i-1]][i-1]; 43 for(int i=Link[x];i;i=e[i].next) 44 { 45 int v=e[i].y; 46 if(v==fa) continue; 47 f[v][0]=x; 48 dfs(v,x); 49 } 50 } 51 int lca(int x,int y) 52 { 53 if(deep[x]<deep[y]) swap(x,y); 54 for(int i=20;i>=0;i--) 55 { 56 if(deep[f[x][i]]>=deep[y]) x=f[x][i]; 57 if(x==y) return x; 58 } 59 for(int i=20;i>=0;i--) 60 { 61 if(f[x][i]!=f[y][i]) 62 { 63 x=f[x][i]; 64 y=f[y][i]; 65 } 66 } 67 return f[x][0]; 68 } 69 int main() 70 { 71 scanf("%d",&n); 72 for(int i=1;i<n;i++) 73 { 74 scanf("%d%d",&xx,&yy); 75 insert(xx,yy,i); 76 insert(yy,xx,i); 77 } 78 79 return 0; 80 }
以上是关于[LCA]倍增法的主要内容,如果未能解决你的问题,请参考以下文章