ST表

0 面向问题

我们希望有一个数据结构能够解决静态区间求最值、gcd....等问题并且可以在 \(O(nlogn)\) 范围内预处理 \(O(1)\) 查询

1 思路

ST表通常维护一些具有可合并性的东西,就是可以分别计算并且不在乎重复计算,比如最大最小值和最大公约数

(但是区间和之类就不行)

以最大值为例

考虑倍增

我们设一个数组 \(F_{i,j}\) 表示从 \(i\) 开始(包括 \(i\))的 \(2^j\) 个数的最大值,即 \([i,i+2^j-1]\) 范围的 \(\max\)

这个东西跟 dp 很像,我们可以看看这玩意怎么转移

注意力稍微集中一下我们很快可以发现 \(F_{i,j}=\max(F_{i,j-1},F_{i+2^{j-1},j-1})\)

边界也十分简单 \(F_{i,0}=a_i\) 其中 \(a\) 是原数组

我们可以码一个简单地预处理:

const int  N=1e5,LGN=31;//数组长度和logn的上界 
#define tn(x) (1<<(x));
//简单地宏定义一下 2^x 这个函数方便用 
void init(){
	LOG[1]=0,LOG[2]=1;
	for(int i=3;i<=n;i++)LOG[i]=LOG[i/2]+1; 
	//此处为预处理log2(n) 的值 
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int j=0;j<=LGN;j++){
		for(int i=1;i+tn(j)-1<=n;i++){
			ST[i][j]=max(ST[i][j-1],ST[i+tn(j-1)][j-1]);
		} 
	}
} 

这里多预处理了一下 \(log2\) 因为后边要用

查询呢?

现在稍微推一下,若查询区间为 \([l,r]\)

我们希望用两个区间,一个从 \(l\) 开始,一个在 \(r\) 结束,并且两个区间能覆盖到 \([l,r]\) 之间的每一个值

假如第一个区间是 \(F_{l,p}\),那么 \(p\) 一定是满足 \(l+2^p-1<=r\) 的最大的数

可求得 \(p=log2(r-l+1)\),不妨取另一个区间为 \(F_{r-2^p+1,p}\),因为 \(p\) 的最大性容易看出这两个区间的确可以覆盖 \([l,r]\)

code:

void qmax(int l,int r){
	int p=LOG(r-l+1);
	return max(ST[l][p],ST[r-tn(p)+1][p]);
}
posted @ 2024-05-12 11:28  exut  阅读(10)  评论(0编辑  收藏  举报
Title