参考课程是我高中信息竞赛邱老师的课程。
【17-2 倍增:ST表】 https://www.bilibili.com/video/BV1DT421a7Ao/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca
【17-1、2 倍增:快速幂、ST表习题解答】 https://www.bilibili.com/video/BV1Wx4y1t7aD/?share_source=copy_web\&vd_source=2c56c6a2645587b49d62e5b12b253dca
需要完整ACM CSP板子可以在资源找,我设置的0积分,如果要钱来B站私我!
ST表(用于查询 解决重复贡献问题 区间最值 区间GCD)
RMQ问题(区间多次询问)【区间动态规划】

扫描效率太低了,求最值就考虑动态规划,FLR为区间最大值,最大值可能是之前区间的最大值FLR-1和当前值aR,的最大值,每次外层for的时候记得输入FLL。

cpp
// 1. 枚举左端点 L
for (int L = 0; L < n; L++) {
// 2. 初始化
F[L][L] = a[L];
// 3. 正着枚举右端点 R (从 L+1 到 n-1) -> 向右扩展
for (int R = L + 1; R < n; R++) {
F[L][R] = max(F[L][R-1], a[R]);
}
}
但是效率还是太低O(N^2)。
考虑倍增。拼凑区间。比如长度为15的区间可以由1 2 4 8拼凑。
还可以优化,重复求取不会影响最值,15的区间可以用从0和7开始长度为8的区间拼凑,中间会重复1,但是没关系。
这样就能使用最多两个预处理区间完成O(1)的搜索。

ST表 P3865
https://www.luogu.com.cn/problem/P3865
两个过程:预处理-查询
预处理
定义Fij状态为以i为起点,长度为2^j的区间最大值,也就是i,i+2\^j-1区间的最大值。确保每次计算的都是2的幂次区间。
初始化时,Fi0以i为起点长度为1的区间最值就是本身ai。
转移方程就是当前区间切两半找最大的值。
查询
拆为两个区间,长度是小于len的最大2次幂,左端点和右端点分别向内找这个区间。


cpp
#include<bits/stdc++.h>
using namespace std;
//要预先处理logN表,存以2为底的下取整数 因为给定长度后要计算其下取证最大二次幂
int logN[100005];
int N,M;
int F[100005][18];
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>N>>M;
cin>>F[1][0];
for(int i=2;i<=N;i++){//对于
cin>>F[i][0];//对于第i个长度为1的最大值就是自身
logN[i]=logN[i>>1]+1;//logN[1]=1 logN[2]=logN[1]+1=2 logN[5]=logN[2]+1=3
}
//执行 预处理 计算各个位置各个长度的区间和
for(int j=1;j<=logN[N];j++){
for(int i=1;i + (1<<j) - 1 <= N;i++){//右边界=i+2^j-1<=N i能算出来
F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
}
}
int L,R;
//执行 查询
for(int i=1;i<=M;i++){
cin>>L>>R;
int loglen=logN[R-L+1];
cout<<max(F[L][loglen],F[R-(1<<loglen)+1][loglen])<<"\n";
}
}