引言
当我们遇到 R M Q (区间最值问题) RMQ(区间最值问题) RMQ(区间最值问题),数组长度为 n n n,我们普遍的想法如下:
- 打擂台,平均时间复杂度: O ( n ) \Omicron(n) O(n)
- 区间排序,平均时间复杂度: O ( n ⋅ log 2 n ) \Omicron(n \cdot \log_2n) O(n⋅log2n)
倘若来个多组访问,程序直接飞起,TLE。
ST表,请开始你的表演
ST表
应用环境
一下条件均符合,方可使用:
- 计算方式必须满足可重复性贡献
- 数组不得修改
可重复性贡献
数组不得修改,这条谁都能理解,但什么是可重复性贡献呢?
简单来说可重复性贡献是指大区间内的值是可以由多个划分出来的可能重叠的小空间并集相互进行相同运算可得。
熟知的 R M Q RMQ RMQ、 g c d gcd gcd问题都是可重复性贡献的
a : 1 , 1 , 2 , 8 , 6 , 5 , 3 , 9 a:1,1,2,8,6,5,3,9 a:1,1,2,8,6,5,3,9
∵ a [ 1 , 3 ] m a x = 2 \because a[1,3]_{max}=2 ∵a[1,3]max=2 & a [ 2 , 6 ] m a x = 8 a[2,6]_{max}=8 a[2,6]max=8
∴ a [ 1 , 6 ] m a x = m a x ( a [ 1 , 3 ] m a x , a [ 2 , 6 ] m a x ) = 8 \therefore a[1,6]_{max}=max(a[1,3]_{max},a[2,6]_{max})=8 ∴a[1,6]max=max(a[1,3]max,a[2,6]max)=8
算法细分
本质上 S T ST ST表,是利用二进制的类似格式去存的,假设现在的运算为区间 m a x max max
状态定义
定义 f i j f_{i j} fij表示从左端点 i i i为始,右端点 i + 2 j − 1 i+2^j-1 i+2j−1为末,长度为 2 j 2^j 2j区间内的 m a x i m u m maximum maximum
状态转移
f i j = m a x ( f i j − 1 , f i + 2 j − 1 j − 1 ) f_{ij}=max(f_{i_{j-1}},f_{{i+2^{j-1}}_j-1}) fij=max(fij−1,fi+2j−1j−1)
将原区间分为2段进行运算
时间复杂度
预处理: O ( n ⋅ log 2 n ) \Omicron(n \cdot \log_2 n) O(n⋅log2n)
单次访问: O ( 1 ) \Omicron(1) O(1)
算法实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m;
int a[maxn],f[maxn][30];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)f[i][0]=a[i];
int p=log2(n);
for(int i=1;i<=p;i++){
for(int j=1;j<=n-(1<<i)+1;j++)f[j][i]=max(f[j][i-1],f[j+(1<<(i-1))][i-1]);
}
//多次访问
while(m--){
int l,r;
scanf("%d%d",&l,&r);
int x=log2(r-l+1);
int ans=max(f[l][x],f[r-(1<<x)+1][x]);
printf("%d\n",ans);
}
return 0;
}
后继
algorithm ++