倍增

跳跳蛙的故事

很久很久以前有一只跳跳蛙,他要从一条长为 \(n\) 笔直的路上的A地跳到B地。

能吃苦的跳跳蛙从A地开始,每次跳一步,终于在很久以后,\(d\) 天之后来到了B地。

又过了几年,跳跳蛙又想去B地。

可是跳跳蛙这次要参加一个紧急会议,一步一步跳未免太慢了,他会被炒鱿鱼的。

跳跳蛙决定第一步跳 \(2^k\) 单位,使得 \(k\) 是最大的满足跳 \(2^k\) 不超过B地的数。

如果他还没有到B地,那么它就再跳 \(2^l\) 单位,使得 \(l\) 是最大的满足跳 \(2^l\) 还不超过B地的数。

如此下去……经过了大约 \(\log _2 d\) 天之后来到了会议室,得到了领导的表扬。

后来,跳跳蛙知道了,原来这种策略就是倍增。

倍增算法

对于每一个点 \(i(1\le i\le n\),跳跳蛙只需要记录 \(c_{i,j}\) 表示从 \(i\) 这个点跳 \(2^j\) 步可以到达的位置。接着他就转移阵地:\(i=c_{i,j}\);于此做循环,直到到达终点。

比如,我们记录 \(c_{1,1}\) 了之后,发现 \(c_{1,2}\) 其实就是 \(c_{2,1}\),所以我们只要想到了怎么转移,\(c\) 数组是很好建立的。需要留心的是,如果 \(c_{i,j}\)\(i+2^j>n\) 那么我们可以直接跳出这层循环因为当 \(j\) 增大时,以后的 \(j\) 永远是 \(i+2^j>n\) 的了。

ST表

【例题】<洛谷>「模板」ST表

建立数组 \(d\),用 \(d_{i,j}\) 表示从 \(i\) 这个位置开始的 \(2^j\) 个数当中的最大值是多少。注意到 \(2^0=1\),我们初始化 \(d_{i,0}=0\)

怎么转移呢?对于 \(d_{i,j}\),他其实是等于 \(\max(d_{i+2^{j-1},j-1},d_{i,j-1})\) 的。

那么对于 \([l,r]\),如何 \(O(1)\) 地求出答案呢?我们考虑,从 \(l\) 开始的 \(2^k\) 个数中的最大值使得 \(k\) 是最大的满足 \(l+2^k-1\le r\) 的数,以及从 \(r\) 开始的往左的 \(2^k\) 个数中的最大值使得 \(k\) 是最大的满足 \(r-2^k+1\ge l\) 的数,取这两个最大值中的较大值,即为答案,因为这两个段一定包含了 \([l,r]\) 中的所有数,由是我们 \(O(1)\) 求得了答案。

代码:

#include <bits/stdc++.h> //这是一道卡常题
using namespace std;
int n,m,l,r,a[100005],d[100005][25],LOG[100005];
inline int read()
{
   int x=0;
   char ch=getchar();
   while(!isdigit(ch)) ch=getchar();
   while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
   return x;
}
inline void print(int x){
   if(x/10) print(x/10);
   putchar(x%10+48);
}
int main()
{
   n=read(),m=read();
   for(int i=1;i<=n;i++) d[i][0]=a[i]=read(),LOG[i]=log2(i);
   for(int i=2,cnt=1;i<=n;i<<=1,cnt++)
       for(int j=1;j+i-1<=n;j++)
           d[j][cnt]=max(d[j][cnt-1],d[j+i/2][cnt-1]);
   while(m--){
       l=read(),r=read();
       int t2=LOG[r-l+1],t1=r-(1<<t2)+1;
       int x=d[l][t2],y=d[t1][t2];
       print(max(x,y));
       puts("");
   }
   return 0;
}

犹待更新,敬请期待...

posted @ 2021-06-30 18:40  pengyule  阅读(203)  评论(0编辑  收藏  举报