(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
",Find(l,r));
}
return 0;
}