求LCA——倍增

Posted 秦时、长浩

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求LCA——倍增相关的知识,希望对你有一定的参考价值。

本代码写的也许有错误,欢迎各位大佬查找其中的错误(位运算不太会)。

  首先在这里不得不提的是——倍增是一种思想,没有一种特殊的框架或者模板之类的东西,有的人认为倍增竟然有模板这种东西。这与这种想法大佬给出的评论是好吧,也许这是来自大佬的嘲讽,或者是说大佬这在传递一种思想。

  倍增思想是一种十分巧妙的思想,在当今的信息学竞赛中应用得十分广泛。本质思想:每次根据已经得到的信息,将考虑的范围扩大一倍,从而加速操作。

  

  我们开始讲倍增求LCA:

      题目:口胡的——题意:给你一个森林,m个询问:v,p。求有多少个点(除v外) 与 v的第p个祖先相同?


    LCA指的是最近公共祖先(Least Common Ancestors),那怎么求呢?最粗鄙的方法就是先dfs一次,处理出每个点的深度,然后把深度更深的那一个点一个点地一个点地往上跳,直到到某个点和另外那个点的深度一样,然后两个点一起一个点地一个点地往上跳,直到到某个点(就是最近公共祖先)两个点“变”成了一个点。
    下面代码就诞生了:

 1 const int POW = 18;  
 2 void dfs(int u , int fa){  
 3     d[u] = d[fa] + 1;  
 4     p[u][0] = fa;  
 5     for(int i = 1; i < POW ; i ++) p[u][i] = p[p[u][i - 1]][i - 1];  
 6     int sz = edge[u].size();  
 7     for(int i = 0 ; i < sz ; i ++){  
 8         int v = edge[u][i];  
 9         if(v == fa) continue;  
10         dfs(v , u);  
11     }  
12 }  
dfs    

不过有没有发现一个点地一个点地跳很浪费时间?如果一下子跳到目标点内存又可能不支持,相对来说倍增的性价比算是很高的,倍增的话就是一次跳2i 个点,不难发现深度差为x时,深度更深的那个点就需要跳x个点。

    我们采用倍增://a ^= b ^= a ^= b;此句感谢ZHT纠错 ; 非位运算乃RQY大佬所改

 1 int lca( int a , int b ){
 2     if( d[a] > d[b] ) a ^= b ^= a ^= b;
 3     if( d[a] < d[b] ){
 4         int del = d[b] - d[a];
 5         for( int i = 0; i < POW ; i ++ ) if(del & (1 << i)) b = p[b][i];
 6     }
 7     if( a != b ){
 8         for( int i = POW - 1; i >= 0; i -- )   
 9             if( p[a][i] != p[b][i] )
10                  a = p[a][i] , b = p[b][i];
11         a = p[a][0] , b = p[b][0];  
12     }  
13     return a;  
14 }  
位运算求 
1 if (d[a] > d[b]) a ^= b ^= a ^= b;
2   if (d[a] < d[b]) {
3     int del = d[a] - d[b];
4     int t = 1, i = 0;
5     while (t < del) t *= 2, ++i;
6     for (; t; t /= 2, --i) if (del >= t) { b = p[b][i]; del -= t; }
7   }
8   //...
非位运算

    注:上下合并一起看是完整代码;

 

以上是关于求LCA——倍增的主要内容,如果未能解决你的问题,请参考以下文章

求LCA——倍增

代码源 Div1 - 105#451. Dis(倍增求LCA)

倍增求LCA

倍增求lca模板

倍增求LCA

倍增求LCA