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)等,对解题有很大帮助。
    相信你已经对倍增了解不少了。要时刻记得,算法是死的而人是活的,必须学会灵活变通,找到题目突破口,才能顺利解题!

    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    HDU 5059 Help him
    HDU 5058 So easy
    HDU 5056 Boring count
    HDU 5055 Bob and math problem
    HDU 5054 Alice and Bob
    HDU 5019 Revenge of GCD
    HDU 5018 Revenge of Fibonacci
    HDU 1556 Color the ball
    CodeForces 702D Road to Post Office
    CodeForces 702C Cellular Network
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910113.html
Copyright © 2011-2022 走看看