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
#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;
}
完