ST表(倍增)

功能
它是解决RMQ问题(区间最值问题)的一种强有力的工具
它可以做到O(nlogn)预处理,O(1)查询最值
原理
利用倍增思想,以最大值为例
用maxx[i][k]表示区间(i,i+2k)最大值;
显然maxx[i][0]=本身;
2k=2*2k-1=2k-1+2k-1;
所以区间(i,i+2k)=(i,i+2k-1)+(i+2k-1,i+2k)
所以可以运用动态转移方程maxx[i][k]=max(maxx[i][k-1],maxx[i+2k-1][k-1]);

//预处理
for(int k = 1; k <= 17; k++){
	for(int i = 1; i+(1<<k)-1<=n; i++){
		maxx[i][k]=max(maxx[i][k-1],maxx[i+(1<<(k-1))][k-1]);
	}
}

查询区间(l,r)
另k=log2(区间长度)=log2(r-l+1);
分别对左右端点进行查询,保证覆盖左右端点;
左端点开始查询,右端点为l+2k , 所以区间为maxx[l][k];
右端点开始查询,我们要找到一个点x,x+2k-1=r;
所以x=r-2k+1, 区间为maxx[x+2k-1][k];
答案就是取maxx[x+2k-1][k],maxx[l][k]大的那个;
代码
理解了原理很好写;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int maxx[100005][30];
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int main(){
	int n,m;
	n=read();
	m=read();
	for(int i = 1; i <= n; i++){
		maxx[i][0]=read();
	}
	for(int k = 1; k <= log2(n); k++){
		for(int i = 1; i+(1<<k)-1<=n; i++){			//注意循环条件
			maxx[i][k]=max(maxx[i][k-1],maxx[i+(1<<(k-1))][k-1]);
		}
	}
	while(m--){
		int l,r;
		l=read(),r=read();
		int k = (int)log2(r-l+1);
		printf("%d\n",max(maxx[l][k],maxx[r-(1<<k)+1][k]));
	}

	return 0;
} 

你可能感兴趣的:(题解)