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;   
}
posted @ 2020-05-18 19:18  Sweetness  阅读(148)  评论(0编辑  收藏  举报