ST表学习笔记
算法简介:\(RMQ\)是一个经典的动态规划问题,是可以\(O(1)\)时间求静态区间最大值的东西,
不过需要\(O(nlog^2n)\)的预处理
算法实现:我们定义\(f_{i,j}\)表示以\(i\)为起点,向后\(2^j\)个数中最大值。两重循环,一重枚举\(i\),一重枚举\(j\)那么可以得到状态转移方程:\(f_{i,j}=max(f_{i,j-1},f_{i+2^{j-1},j-1})\)其中初始值为\(f_{i,0}=a_i\)。接下来开始查询,如果我们要查询的区间为\(x\)到\(y\),那么先求出\(log^2(x-y+1)=k\),再在\(f_{x,k}\)与\(f_{y-2^k+1,k}\)之间取最大值,因为两端区间会重合,但这个重合没有关系。
查询\(1-12\)的最大值,那么求出\(log^2(x-y+1)=3\),那么在\(f_{1,3}\)与\(f_{5,3}\)之间去最大值,虽然有重合,但没有关系,因为最大值不会被算两次。
个人理解:作为一个\(dp\)产生的数据结构,这个思想无疑是极优的。尤其是重合不会影响答案的思想,更为巧妙,但这也局限了他的用途,毕竟不是每个问题都重合不会影响答案。
代码实现:
#include<cstdio>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int n,a[50039],stin[50039][39],x,y,z,m,stax[50039][39],maxn,minn;
inline void read(register int &x){
x=0;register char s=getchar();
while(s<'0'||s>'9') s=getchar();
while(s>='0'&&s<='9') x=(x<<3)+(x<<1)+(s^48),s=getchar();
}
inline void print(register int x) {
if(x>9) print(x/10);
putchar(x%10+'0');
}
int main(){
register int i,j,k;
read(n);read(m);
for(i=1;i<=n;i++)read(a[i]),stin[i][0]=a[i],stax[i][0]=a[i];
for(i=n-1;i>=1;i--){
for(j=1;i+(1<<j)-1<=n;j++){
stax[i][j]=max(stax[i][j-1],stax[i+(1<<j-1)][j-1]);
stin[i][j]=min(stin[i][j-1],stin[i+(1<<j-1)][j-1]);
}
}
for(i=1;i<=m;i++){
read(x);read(y);
k=0;
while(x+(1<<k+1)-1<=y) k++;
maxn=max(stax[x][k],stax[y-(1<<k)+1][k]);
minn=min(stin[x][k],stin[y-(1<<k)+1][k]);
print(maxn);
putchar('\n');
print(minn);
putchar('\n');
}
return 0;
}