Luogu P3865 【模板】ST表
\(Luogu\) \(P3865\) 【模板】\(ST\)表
ST 表是用于解决 可重复贡献问题 的数据结构。
可重复贡献问题 是指对于运算\(opt\),满足\(x \ opt \ x=x\),则对应的区间询问就是一个可重复贡献问题。例如,最大值有\(max(x,x)=x\),\(gcd\) 有\(gcd(x,x)=x\),所以\(RMQ\) 和区间\(GCD\)就是一个可重复贡献问题。像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次,这是我们所不愿意看到的。另外,\(opt\)还必须满足结合律才能使用\(ST\)表求解。
\(RMQ\)是英文 \(Range\ Maximum/Minimum\ Query\)的缩写,表示区间最大(最小)值。解决\(RMQ\)问题有很多种方法,可以参考 RMQ 专题 。
——引自\(OI\) \(Wiki\)
\(ST\)表适用于没有修改的区间最大值问题,当然如果数据强度不高,树状数组和线段树也是一个很好地选择。
线段树做法(\(\cal {LZ}\)有话说:大概是自己的线段树常数太大,并不能快乐的\(AC\)
\(ST\)表基于倍增的思想,可以做到\(O(nlogn)\)预处理,\(O(1)\)查询。
记录\(ST[i][j]\)为从\(j\)开始往后数\(2^i\)个这些数的最大值
显然\(ST[i][j]\)可以递推求出:\(ST[i][j]=max(ST[i-1][j],ST[i-1][j+2^{i-1}]);\)
初始:\(ST[0][i]=a[i];\)
求一个区间\([l,r]\)的最大值:
\(\frak{a}\).求出区间长度:\(k=l-r+1\);
\(\frak{b}\).记录\(p=log_2k\);
\(\frak{c}\).求这个区间的最大值\(max(ST[p][l],ST[p][r-2^p+1])\);
\(log_2x\)的处理:
Log[0]=-1;
for(int i=1;i<=n;++i)
Log[i]=Log[i>>1]+1;
#include<bits/stdc++.h>
#define N 100005
#define K 21
using namespace std;
int m,n,p,k,ST[K+1][N],a[N],Log[N];
inline int read() {
int ans=0;
char last=' ',ch=getchar();
while(ch>'9'||ch<'0') last=ch,ch=getchar();
while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar();
if(last=='-') ans=-ans;
return ans;
}
int Find(int l,int r)
{
int x=Log[r-l+1];
return max(ST[x][l],ST[x][r-(1<<x)+1]);
}
int main() {
n=read();m=read();
Log[0]=-1;
for (int i=1;i<=n;++i) {
ST[0][i]=read();
Log[i]=Log[i>>1]+1;
}
for (int i=1;i<=K;++i)
for (int j=1;j+(1<<i)-1<=n;++j)
ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1<<(i-1))]);
for(int i=1,l,r;i<=m;++i) {
l=read();r=read();
printf("%d\n",Find(l,r));
}
return 0;
}