学习笔记——ST算法

ST 算法

ST 算法是一种运用倍增来解决 RMQ 问题也就是区间最值问题的算法。
给定一个长度为 \(N\) 的序列 \(A\),ST 算法能在 \(\mathcal O(N \log N)\) 的时间预处理后,以 \(\mathcal O(1)\) 的时间在线回答区间最值问题。
\(F_{i,j}\) 表示序列 \(A\) 中下标在子区间 \(\left[i,i + 2^j - 1 \right]\) 里的数的最大值,也就是从 \(i\) 开始 \(2^j\) 个数的最大值。边界是 \(F_{i,0} = A_i\)
在递推时我们将子区间的长度成倍增长,\(F_{i,j} = \max(F_{i,j - 1},F_{i + 2^{j-1},j - 1})\),即长度为 \(2^j\) 的子区间的最大值是左右两边长度为 \(2^{j-1}\) 的子区间的最大值中大的那一个。

for(int i=1;i<=n;i++){
	f[i][0]=a[i];
}
for(int j=1;(1<<j)<=n;j++){
	for(int i=1;i+(1<<j)-1<=n;i++){
		f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	}
}

当询问区间 \(\left[l,r \right]\) 的最值时,先找出一个 \(k\),满足 \(2^k \le r - l + 1 \le 2^{k+1}\),那么从 \(l\) 开始的 \(2^k\) 个数和以 \(r\) 结尾的 \(2^k\) 个数一定覆盖了整个区间。
而这两段的最大值分别为 \(F_{l,k}\)\(F_{r - 2^k + 1,k}\),所以整个区间的最值就是这两段中大的那一个。

int ST(int l,int r){
	int k=log(r-l+1)/log(2);
	return max(f[l][k],f[r-(1<<k)+1][k]);
}

Example

这里以Luogu P3865为例。
一道模版题,直接看代码就行。

#include<bits/stdc++.h>
using namespace std;
long long n,m,a[100005],l,r,f[100005][155];
int ST(int l,int r){
	int k=0;
	while(l+(1<<k+1)-1<=r){
		k++;
	}
	return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		f[i][0]=a[i];
	}
	for(int j=1;(1<<j)<=n;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
	}
	for(int i=1;i<=m;i++){
		cin>>l>>r;
		cout<<ST(l,r)<<'\n';
	}
	return 0;
}
posted @ 2024-01-17 21:47  GyrthCurunír  阅读(28)  评论(0编辑  收藏  举报