st表

一、算法简析

功能:主要用于查询区间的最值、\(gcd\)\(lcm\)等。
效率:预处理 \(O(nlogn)\),查询 \(O(1)\)

注:\(st\) 表用于区间查询。若要区间修改,要用线段树

以区间最大值为例,介绍 \(st\) 表。

预处理

\(st[i][j]=\) 以第 \(i\) 数为起点,长度为 \(2^j\) 的区间最大值。 数列长度为 \(N\)
一个长度为 \(2^j\) 的区间,可以分成两个长度为 \(2^{j-1}\) 的区间,则递推式为

\[st[i][j]=max(st[i][j - 1], st[i + 2^j][j - 1]) \]

  • 预处理 \(\lfloor log_2(x) \rfloor\)

\[\lfloor log_2(x) \rfloor = \lfloor log_2(\frac{x}{2}\times 2) \rfloor = \lfloor log_2(\frac{x}{2}) \rfloor + 1 \]

// 预处理log_2(x),向下取整 
lg2[1] = 0;
for (int i = 2; i <= N; ++i)    lg2[i] = lg2[i >> 1] + 1;

\(x\) 的最大值为数列长度 \(N\)

  • 预处理 \(2^{x}\)

\[2^x=2^{x-1}\times2 \]

// 预处理2^(x)
pw2[0] = 1;
for (int i = 1; i < 30; ++i)    pw2[i] = pw2[i - 1] << 1;

\(x\) 的最大值满足 \(2^x \ge N\)

  • 预处理 \(st\)
// 预处理st
for (int i = 1; i <= N; ++i)    st[i][0] = quickin();
for (int j = 1; j <= lg2[N]; ++j) // 先枚举区间长度 
    for (int i = 1; i + pw2[j] - 1 <= N; ++i) // 再枚举区间起点 
	st[i][j] = max(st[i][j - 1], st[i + pw2[j - 1]][j - 1]);

区间左端点 \(i\) 满足:区间右端点必须小于等于 \(N\),即 \(i + 2^j - 1 <= N\)
区间长度 \(2^j\) 的值满足:区间长度小于等于 \(N\),即 \(2^j\le N\),可转换为 \(j \le \lfloor \log_2(N) \rfloor\)

查询

对于区间 \([L, R]\)\(k=\lfloor \log_2(R - L + 1) \rfloor\),必然满足 \(2^k\le R - L + 1 < 2 \times 2^k\)

因此,查询区间 \([L, R]\) 的最大值,可以分别查询两个长度为 \(2^k\) 的区间的最大值,再取最大值。为了保证两个区间的叠加可以完全覆盖区间 \([L, R]\),令一个区间的左端点固定为 \(L\),另一个区间的右端点固定为 \(R\)。此时,\([L, R]\) 的中间某一段是重叠的,但不影响最终结果。

// 查询 
int query(int L, int R)
{
	int k = lg2[R - L + 1];
	return max(st[L][k], st[R - pw2[k] + 1][k]);
}

Code

P3865 【模板】ST 表

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

ll quickin(void)
{
	ll ret = 0;
	bool flag = false;
	char ch = getchar();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-')    flag = true;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9' && ch != EOF)
	{
		ret = ret * 10 + ch - '0';
		ch = getchar();
	}
	if (flag)    ret = -ret;
	return ret;
}

const int MAX = 1e5 + 5;
int st[MAX][30], lg2[MAX], pw2[30]; // st[i][j] -- 以i为起点,2^j为长度 
int N, M;

void init(void)
{
	// 预处理log_2(x),向下取整 
	lg2[1] = 0;
	for (int i = 2; i <= N; ++i)    lg2[i] = lg2[i >> 1] + 1;
	// 预处理2^(x)
	pw2[0] = 1;
	for (int i = 1; i < 30; ++i)    pw2[i] = pw2[i - 1] << 1;
	
	// 预处理st
	for (int i = 1; i <= N; ++i)    st[i][0] = quickin();
	for (int j = 1; j <= lg2[N]; ++j) // 先枚举区间长度 
		for (int i = 1; i + pw2[j] - 1 <= N; ++i) // 再枚举区间起点 
			st[i][j] = max(st[i][j - 1], st[i + pw2[j - 1]][j - 1]);
}

// 查询 
int query(int L, int R)
{
	int k = lg2[R - L + 1];
	return max(st[L][k], st[R - pw2[k] + 1][k]);
}

int main()
{
	#ifdef LOCAL
	freopen("test.in", "r", stdin);
	#endif
	
	N = quickin(), M = quickin();
	
	// 预处理 n.
	init();
	
	// 查询
	for (int i = 0; i < M; ++i)
	{
		int a, b;
		a = quickin(), b = quickin();
		printf("%d\n", query(a, b));
	}
	
	return 0;
}

posted @ 2024-05-27 09:32  ltign  阅读(7)  评论(0编辑  收藏  举报