倍增算法详解

定义

倍增法,顾名思义就是翻倍。它能够使线性的处理转化为对数级的处理,大大地优化时间复杂度。
这个方法在很多算法中均有应用,其中最常用的是 RMQ (Ranged Max/Min Query) 问题和求
LCA(Lowest Common Ancestor)

引例

在你面前的桌子上,摆着无数个重量为任意整数的胡萝卜;
接着告诉你一个数字n,问你要怎么挑选,使得你选出的胡萝卜能够表示出 [1,n] 区间内的所有整数重量?

读完题后我们马上就能想到一种选法,那就是选n个重量为1的胡萝卜,这样就能通过加减表示出 [1,n] 内的所有重量了。

但问题是……这样挑选的胡萝卜是不是太多了点?

我们很快就能发现,只需要选择重量为 1,2,4,8 的胡萝卜,就能表示 [1,31] 内的所有重量。所以,只需要选择重量 20,21,22……2n 的胡萝卜,就能表示 [1,n] 内的所有重量。

也就是说, 对于给定的数字 ,根本不需要选那么多胡萝卜,只需要 个胡萝卜就够啦!

由此引例我们得出一个结论:只需要 的预处理,就能表示出 区间内的所有情况。


Vector扩容机制

std::vectorpush_back() 操作是均摊线性的,其实现原理是每次 sizecapability 相等并执行 push_back() 操作时,将申请一块约2倍大小的新空间,这样总 memcpy 操作的内存大小即是:

O ( N ) + O ( N / 2 ) + . . . + O ( 1 ) = O ( N ) O(N)+O(N/2)+...+O(1)=O(N) O(N)+O(N/2)+...+O(1)=O(N)

解法

  • ST表

    模板 代码:

          /*
          ST图 模板
          此模板以(洛谷)P2880 [USACO07JAN] Balanced Lineup G 为标准
          通用大部分ST图的题 
          */ 
          #include <iostream> 
          #include <algorithm>
          #include <cmath>
          using namespace std;
          #define int long long
    
          const int maxn=1e5+10; //此处可以修改 
          const int lg_maxn=__lg(maxn)+10;
    
          int a[maxn];
          int f1[maxn][lg_maxn]; //max
          int f2[maxn][lg_maxn]; //min
    
          void init() //计算 
          {
              for (int i=1;i<=n;i++) f1[i][0]=f2[i][0]=a[i];
              int k=log(n)/log(2);
              for (int j=1;j<=k;j++)
              {
                  for (int i=1;i<=n-(1<<j)+1;i++)
                  {
                      f1[i][j]=max(f1[i][j-1],f1[i+(1<<(j-1))][j-1]); //此处可以修改 
                      f2[i][j]=min(f2[i][j-1],f2[i+(1<<(j-1))][j-1]); //此处可以修改 
                  }
              } 
          }
    
          int query(int l,int r) //查找 
          {
              int p=log(r-l+1)/log(2);
              return max(f1[l][p],f1[r-(1<<p)+1][p])-min(f2[l][p],f2[r-(1<<p)+1][p]); //此处可以修改 
          }
    
          signed main()
          {
              int n,m;
              cin >> n >> m;
              for (int i=1;i<=n;i++) cin >> a[i];
              init();
              for (int i=1;i<=m;i++) 
              {
                  int l,r;
                  cin >> l >> r;
                  cout << query(l,r) << endl;
              }
              return 0;
          }
    
          /*
          2023/12/13 10:29 修正1
          2023/12/13 10:39 修正2
          */ 
      ```
    

练习题

  1. P3865 【模板】ST 表
  2. P1816 忠诚
  3. P1890 gcd区间
  4. P2880 [USACO07JAN] Balanced Lineup G
  5. P2471 [SCOI2007] 降雨量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值