【知识点】ST表

引入

ST表(Sparse Table, 稀疏表)是一种数据结构,用于解决可重复贡献问题(RMQ)。

什么是可重复贡献问题?

对于一串数字,需要计算某个区间的数字依次进行某个二元运算最终得到的结果。如果这个运算符合幂等律\(x * x = x\))和结合律\((x * y) * z = x * (y * z)\)),那么它就有可重复贡献的性质。

可重复贡献:即使区间内某些数重复参与运算,也不影响结果。

举个例子,求最大值运算\(max(x, y)\),符合幂等律:\(max(x, x) = x\), 符合结合律:\(max(max(x, y), z) = max(x, max(y, z))\),则求最大值运算具有可重复贡献的性质。如果要计算下标从\(1\)\(12\)的区间的最大值,那么计算从\(1\)\(8\)的最大值\(a\),再计算从\(5\)\(12\)的最大值\(b\),则最终结果为\(max(a, b)\)。在这个过程中,即使从\(5\)\(8\)的区间被重复计算,也不影响最终答案。

常见的可重复贡献问题有:区间最值、区间按位和、区间按位或、区间GCD等。

原理

构建

ST表基于倍增的思想。我们需要计算以第\(i\)个位置为左端点,长度为\(2^j\)的区间的计算结果。

\(st[i][j]\)表示以第\(i\)个位置为左端点,长度为\(2^j\)的区间的计算结果。

状态转移方程为:\(st[i][j] = st[i][j-1] * st[i + 2^{j-1}][j-1]\)

查询

如果需要查询\([l, r]\)区间的计算结果,需要先计算区间长度\(len = r - l + 1\)。然后计算出\(log_2len\)向下取整,\(2^{log_2len}\)作为被查询的区间长度。一般计算\(log_2len\)是被预处理的,可以保证查询的低时间复杂度。

举个例子,查询\(1\)\(12\)的计算结果,先计算长度\(len = 12\),然后查出\(log_2len = log_212 = 3\),则\(2^{log_2len} = 2^3 = 8\)为被查询区间的长度,我们只需要查询\(1\)\(8\)的结果和\(5\)\(12\)的结果,然后进行运算即可。

代码实现

构建

for (int i = 1; i <= n; i++)
	cin >> st[i][0];
for (int j = 1; j <= logn[n]; j++)
	for (int i = 1; i + (1 << j) - 1 <= n; i++)
		st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);	//倍增递推

构建的时间复杂度为\(O(nlogn)\)

查询

//预处理logn
logn[1] = 0, logn[2] = 1;
for (int i = 3; i <= n; i++)
	logn[i] = logn[i / 2] + 1;

//接收查询
cin >> l >> r;
len = r - l + 1;	//计算区间长度
ans =  max(st[l][logn[len]], st[r - (1 << logn[len]) + 1][logn[len]]);
//注意两个区间的下标

预处理的时间复杂度\(O(n)\)

查询的时间复杂度为\(O(1)\)

模板题AC代码

洛谷 ST表模板题

AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

int n, m, st[100100][35], logn[100100], l, r, len;

inline void init() {
	logn[1] = 0, logn[2] = 1;
	for (int i = 3; i <= n; i++)
		logn[i] = logn[i / 2] + 1;
	return;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	init();
	for (int i = 1; i <= n; i++)
		cin >> st[i][0];
	for (int j = 1; j <= logn[n]; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
	while (m--) {
		cin >> l >> r;
		len = r - l + 1;
		cout << max(st[l][logn[len]], st[r - (1 << logn[len]) + 1][logn[len]]) << '\n';
	}
	return 0;
}

评测记录

posted @ 2025-08-11 16:44  Alkaid16  阅读(66)  评论(0)    收藏  举报