ST表学习笔记

发布时间 2023-12-19 11:43:40作者: Creeper_l

模板题:P3865

定义

\(ST\)表是一种解决可重复贡献的问题的数据结构。可重复贡献问题大致指,对于一种运算,重复这种运算并不影响最终的答案,比如\(max(a,a) = a\)\(gcd(a,a) = a\)。常见的可重复贡献问题有:区间\(max\),区间\(min\),区间\(gcd\),这些可以用\(ST\)表来维护。显然,区间加不属于可重复贡献问题。

思路

如果用暴力来处理答案的话时间复杂度是O(\(n^{2}\)),显然会T。
于是考虑用倍增来优化,先用\(O(n log n)\)的时间预处理出倍增数组,再用\(O(1\))的时间查询。

做法

\(f_{i,j}\)表示下标从\(i\)\(i + 2^{j} - 1\)中的最值,易得\(f_{i,0} = a_{i}\)。然后考虑用倍增来递推,根据\(f\)数组的定义可知,f数组的第二维就相当于跳了\(2^{j} - 1\)步,于是可以得到递推方程式:\(f_{i,j} = f_{i,j - 1} + f_{i + 2^{j-1},j - 1}\)。可以在\(log n\)的时间复杂度中预处理出。

然后考虑如何来查询。首先我们知道区间最值是一种可重复贡献的问题,所以查询的区间即使有重叠也没关系。对于查询的每一组区间\([l,r]\),可以将它分为两个子区间,\([l,l + 2^{j-1} - 1]\)\([r - 2^{j-1} + 1][r]\),用这两个区间的值算出\([l,r]\)区间的值。这里的\(s = log(r - l + 1)\),这样才能保证没有漏掉的数。所以,\(ans = max(f_{l,s},f_{r - 2^{j-1} + 1,s})\)\(s\)在最初预处理出即可。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef pair <int,int> pii;
const int MAXN = 2e6 + 10;
int Log[MAXN],f[MAXN][25],n,m,l,r;
void pre()
{
	Log[1] = 0,Log[2] = 1;
	for(int i = 3;i < MAXN;i++) Log[i] = Log[i / 2] + 1;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i = 1;i <= n;i++) scanf("%lld",&f[i][0]);
	pre();
	for(int j = 1;j <= 25;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++)
	{
		scanf("%lld%lld",&l,&r);
		int s = Log[r - l + 1];
		printf("%lld\n",max(f[l][s],f[r - (1 << s) + 1][s]));
	}
	return 0;
} 

总结

\(ST\)表的优点是时间复杂度较低,代码量相对其他算法很小,还可以做到\(O(1)\)查询。但是,\(ST\) 表能维护的信息非常有限,不能较好地扩展,并且不支持修改操作。