zoukankan      html  css  js  c++  java
  • 倍增 思想与操作

    倍增

    倍增是把时间复杂度为 O ( n ) O(n) O(n)的一个操作变为 O ( log ⁡ 2 n ) O(\log_2n) O(log2n)的操作。
    它与分治的思想类似,时间复杂度也类似。
    它们的区别如下:
    分治是将一个问题分成若干个子问题,最后的答案由子问题的答案合并得到。
    倍增是将一个需要执行多次的操作分解,操作的结果直接由两个子操作的结果得到。

    例子

    最常见的例子就是在树上倍增。
    每个询问要求节点 x x x向上跳 s s s次到达的节点编号。
    若每次暴力向父亲节点跳,一共跳 s s s次,时间复杂度特别大。
    这时就要使用倍增。

    如何实现

    维护每个节点向上跳可到达的节点编号。
    f [ i ] [ j ] f[i][j] f[i][j]表示节点 i i i向上跳 2 j 2^j 2j次到达的节点编号。
    所有的 f [ i ] [ 0 ] f[i][0] f[i][0]自然就为 f a t h e r [ i ] father[i] father[i] i i i的父亲节点),其余的如何转移?
    我们可以发现,向上跳 2 j 2^j 2j次就相当于是先向上跳 2 j − 1 2^{j-1} 2j1次,再向上跳 2 j − 1 2^{j-1} 2j1次。
    于是我们不难得到转移方程:
    f [ i ] [ j ] = f [ f [ i ] [ j − 1 ] ] [ j − 1 ] f[i][j]=f[f[i][j-1]][j-1] f[i][j]=f[f[i][j1]][j1]

    void dfs(int i,int fa)
    {
    	f[i][0]=fa;
    	for(j=1;j<=log(n);j++) f[i][j]=f[f[i][j-1]][j-1];
    	for(j=last[i];j;j=next[j]) if(a[j]!=fa) dfs(a[j],i);	
    }
    

    怎么跳?
    类似十进制转二进制的方式,从大到小枚举 j j j,如果 s ≥ 2 j s≥2^j s2j就把 x x x跳到 f [ x ] [ j ] f[x][j] f[x][j],并且把 s − 2 j s-2^j s2j

    for(j=log(n);j>=0;j--)
    {
    	if(s>=p[j]) 
    	{
    		x=f[x][j];
    		s-=p[j];
    	}
    }
    

    总结

    可以用倍增实现的还有很多,如树上最大值、树上两点的最近公共祖先(LCA)等,对解题有很大帮助。
    相信你已经对倍增了解不少了。要时刻记得,算法是死的而人是活的,必须学会灵活变通,找到题目突破口,才能顺利解题!

    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    向eureka注册正确的ip地址
    sleuth + zipkin 链路分析
    Yii2的整体结构概览
    Redis实现消息队列
    Redis使用场景梳理
    redis基础知识
    TCP服务
    数据结构-队列
    看见
    线性表的链式存储结构
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910113.html
Copyright © 2011-2022 走看看