// //

数据结构——关于倍增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 }
[Ahoi2008]Meet 紧急集合

 

  总结一下:倍增LCA的用途其实比较单调,因为会各种被卡

但是我等蒟蒻别无他法(tarjan老贼出来挨打),所以这个算法会贯穿

我们整个树上问题,学会他是很必要的(当然大佬们可能已经学会了LCA的四种求法,那就当我没说qwq)

 

  end;

 

posted @ 2019-07-30 10:58  Zafkiel  阅读(240)  评论(0编辑  收藏  举报
Live2D //博客园自带,可加可不加