首先放一道模板题
这看上去特别简单,直接暴力就可以。
#include<bits/stdc++.h>
using namespace std;
int a[100001],m,n,t,k,ans=9999999;
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
scanf("%d%d",&t,&k);
for(int j=t;j<=k;j++)ans=min(ans,a[j]);
printf("%d ",ans);
ans=9999999;
}
}
但是提交之后,你会惊奇的发现,90分!太高了怎么优化呢?
这个时候,ST表就出场了(square table)
首先一个二维数组,定义f[i][j]为a[i]到a[i+1<<j-1]的最小值,只要你学过一点倍增,你就会觉得下面很好理解,当然,没学过也没关系,我们可以利用动态规划的思想,写出动态转移方程式。
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);就相当于把这个区间切成两个部分。
接下来,双重循环,就可以了。
这样子,我们就有了一个ST表,当然,怎么查询呢?
很简单,你把查询的区间分成两个部分,再直接在ST表里面调用。
int k=log2(r-l+1);
return min(f[l][k],f[r-(1<<k)+1][k]);
好了,思路写完了,你可以开始自己写代码了。
这道题写完了,你肯定也会写区间最大值了,这种查询区间最大值和最小值的问题就是大名鼎鼎的RMQ。
除了最大值和最小值之外,ST还是用于解决 可重复贡献问题 的数据结构。
你可能问,可重复贡献问题是啥,请看下面
可重复贡献问题 是指对于运算 opt,满足x opt x =x,则对应的区间询问就是一个可重复贡献问题。例如,最大值有 max(x,x)=x,gcd 有 gcd(x,x)=x,所以 RMQ 和区间 GCD就是一个可重复贡献问题。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次,这是我们所不愿意看到的。另外,opt还必须满足结合律才能使用 ST 表求解。
人话是什么?调用ST表时,大多数时候都会重合,而可重复贡献问题指的就是重合对结果正确性不产生影响的问题。
例题: