浅谈倍增算法

 


倍增(ST)

倍增是什么?

倍增,每次将范围扩大或减少一倍以达到加速的效果

思想引入:如果你想跳到n = 15米远的地方,怎么做?

  1. 一步一步跳过去(暴力):显然这是15次

  2. 2k次(倍增):

    1. 设 k = 5,25 = 32 > n, k -- ,k = 4 (超了)
    2. k = 4, 24 = 16 > n, k --,k = 3 (超了)
    3. k = 3,23 = 8 < n,n = n - 8 = 7, k --, k = 2(跳)
    4. k = 2,22 = 4 < n,n = n - 4 = 3, k --, k = 1(跳)
    5. k = 1,21 = 2 < n,n = n - 2 = 1, k --, k = 0(跳)
    6. k = 0,20 = 1 < n,n = n - 1 = 0(跳到了)

    即只跳了4次,与暴力做法相比,少了11次。而且数据越大相差的越大,因为暴力是O(n)而倍增是O(logn)

    可以发现一个性质,如果跳用1,不跳用0表示,则用二进制表示为1111,这恰好是15。

    所以这就称为二进制转换,比如11用倍增的思想做出来,恰好是1011.

在图论中的应用

在大多数情况下,图论中的点或边都可以按某种方式排序,如果问题要求的是O(nlogn)的时间复杂度,就需要考虑倍增的思想,可以结合dp的最优子结构和ST表的思想求解。

倍增在LCA(最近公共祖先)中的应用

LCA其实就相当于在树上找uv的最短路,因为找到了最近公共祖先就相当于找到了一条最短路径

首先,要找到uv第一个不同祖先不同的位置,然后从这个位置向上走一步就是最近公共祖先。要想找到u,v第一个不同祖先的位置,就要保证u,v在同一深度(这样才能同时向上移一步)

所以这个过程分为三步:

1. 预处理找到每个节点深度 2. 把较深的一点移动到与较浅一点的高度 3. 两个一起往上移动直到它们的父亲节点相同

先用一个dfs/bfs找到所有的节点深度,用depth数组存下

fa[i,j]表示从i开始,向上走2j步所能走到的节点。

预处理

void bfs(int root) { memset(depth,0x3f,sizeof depth); depth[0] = 0,depth[root] = 1; int hh = 0,tt = 0; q[0] = root; while(hh <= tt) { int t = q[hh ++]; for (int i = h[t];~i;i = ne[i]) { int j = e[i]; if (depth[j] > depth[t] + 1) { depth[j] = depth[t] + 1; q[ ++ tt] = j; fa[j][0] = t; for (int k = 1;k <= 15;k ++) fa[j][k] = fa[fa[j][k - 1]][k - 1]; } } } }

查询

int lca(int a,int b) { if (depth[a] < depth[b]) swap(a,b); for (int k = 15;k >= 0;k --) if (depth[fa[a][k]] >= depth[b]) a = fa[a][k]; if (a == b) return a; for (int k = 15;k >= 0;k --) if (fa[a][k] != fa[b][k]) { a = fa[a][k]; b = fa[b][k]; } return fa[a][0]; }

__EOF__

本文作者Zhicheng
本文链接https://www.cnblogs.com/L-zhicheng/p/16222138.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zhicheng_1003  阅读(470)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示