数据结构——关于倍增LCA那点事
关于LCA那点事~~
首先我们要了解LCA为什么
LCA所求是公共最近祖先
听上去可能很玄学..............
其大意即为给定一棵树,在上面选一些点,他们所有人的最近
父亲.
首先我们整理思路,思考一些可行的方案~~~~~
1.蒟蒻:我会暴力!!
找到点一步一步先把所有点提到同一个deep处,然后大家
同步往上跳,直到大家重合.
时间复杂度O(n*k);//k为查询次数
died;
2.小牛:我会RMQ,不会LCA!!
同学你好好听课
3.中牛:我会LCA!!
同学哪种求法?? 中牛:暴力!!
抬走下一位
4.大牛:我会tarjan_LCA!!!
说的好我不会.....
抬走下一位
5.又一只蒟蒻:我会倍增LCA
if(倍增LCA[蒟蒻]==true) 蒟蒻=Zafkiel
很好我们就来学习倍增LCA好了 qwq
倍增LCA
倍增LCA的基本思路很好,代码也不是很难,但是
容易被卡常,但是对于一只非常蒻的蒟蒻来说已经是很好的算法了
LCA的思想:倍增LCA的思想和RMQ的思想是非常接近的,基本思路
就是我们用log优化向上跳时所跳的距离,使其不用一个一个的跳,这就
使时间复杂度减了很多,变为O(logn),就很好;
思路:首先建树,深搜求树上点的深度,然后写出LCA函数,即可
看似简单....
来道例题
[Ahoi2008]Meet 紧急集合
题目描述太长了,我们简化一下
给定一棵树,给定三个点,求三个点的LCA和三个点到
目标点所需步数.
思路:三个点两两求LCA,最后差分计算步数即可
1 #include <bits/stdc++.h> 2 using namespace std; 3 long long ans; 4 int dis[500010][25],lg[500010],deep[500010],t; 5 struct node{ 6 int to; 7 int nxt; 8 }edge[1000010]; 9 int head[1000010],cnt; 10 inline void add(int from,int to){ 11 cnt++; 12 edge[cnt].to=to; 13 edge[cnt].nxt=head[from]; 14 head[from]=cnt; 15 } 16 inline int lca(int x,int y){ 17 if(deep[x]<deep[y]) swap(x,y);//假设x深度大于y 18 while(deep[x]>deep[y]) x=dis[x][lg[deep[x]-deep[y]]-1];//x,y调整到同一深度 19 if(x==y) return x;//深度相同则直接返回 20 for(register int k=lg[deep[x]];k>=0;k--){//x,y一起向上跳 21 if(dis[x][k]!=dis[y][k]){ 22 x=dis[x][k]; 23 y=dis[y][k]; 24 } 25 } 26 return dis[x][0]; 27 } 28 void dfs(int x,int fa){ 29 deep[x]=deep[fa]+1;//处理深度 30 dis[x][0]=fa; 31 for(register int i=1;(1<<i)<=deep[x];i++) dis[x][i]=dis[dis[x][i-1]][i-1]; 32 for(register int i=head[x];i;i=edge[i].nxt) if(edge[i].to!=fa) dfs(edge[i].to,x); 33 } 34 int n,m; 35 int a,b,c; 36 int main(){ 37 scanf("%d%d",&n,&m); 38 for(register int i=1;i<=n-1;i++){ 39 scanf("%d%d",&a,&b); 40 add(a,b); 41 add(b,a); 42 } 43 dfs(1,0); 44 for(register int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);//常数优化 45 for(register int i=1;i<=m;i++){ 46 ans=0; 47 scanf("%d%d%d",&a,&b,&c); 48 int t1=lca(a,b);//三者分别求LCA 49 int t2=lca(a,c); 50 int t3=lca(b,c); 51 if(t1==t2) t=t3; 52 else if(t1==t3) t=t2; 53 else if(t2==t3) t=t1; 54 ans=deep[a]+deep[b]+deep[c]-deep[t1]-deep[t2]-deep[t3];//差分 55 printf("%d %lld\n",t,ans); 56 } 57 return 0; 58 }
总结一下:倍增LCA的用途其实比较单调,因为会各种被卡
但是我等蒟蒻别无他法(tarjan老贼出来挨打),所以这个算法会贯穿
我们整个树上问题,学会他是很必要的(当然大佬们可能已经学会了LCA的四种求法,那就当我没说qwq)
end;