ST表
\(ST\)表\((Sparse\ Table,稀疏表)\)是一种数据结构,主要用来解决\(RMQ(Range\ Maximum/Minimum\ Query,区间最大/最小值查询)\)问题。实现的主要思想是倍增,时间复杂度\(O(nlogn)\)预处理,\(O(1)\)查询。但不支持修改操作。
问题引入
P3865 【模板】ST 表
给定一个长度为 \(N\) 的数列,和 \(M\) 次询问,求出每一次询问的区间内数字的最大值。
解题思路
\(ST\)使用一个二维数组存储一定范围内的最大/小值,例如\(f[i][j]\)表示区间\([i,\ i+2^{j})\)的最值,注意区间是左闭右开。
该数组的赋值称为预处理,代码如下:
for (int i = 1; i <= lg[n]; ++i)
{
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
{
f[j][i] = max(f[j][i - 1], f[j + (1 << i - 1)][i - 1]);
}
}
基本思想就是倍增,一个区间的最值来自在左半部分与右半部分的最值。
接下来就是查询,对于区间\([l,\ r]\),我们令\(s=log_2(r-l+1)\),那么可以证明\([l+2^s,\ r-2^s+1]\)必然会覆盖原来的区间,尽管可能会有重叠部分,但这并不影响结果。查询代码如下:
rd(l);rd(r);
int s = lg[r - l + 1];
printf("%d\n",max(f[l][s], f[r - (1 << s) + 1][s]));
完整代码
#include <bits/stdc++.h>
using namespace std;
#define gt getchar()
const int N = 1e1 + 10;
int f[N][20];
int lg[N];
template<typename T>
void rd(T &x)
{
x = 0;
int f = 1;
char ch = gt;
while (ch < '0' || ch > '9'){if (ch == '-')f = -1;ch = gt;}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0'; ch = gt;}
x = x * f;
}
int main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
int n, m;
rd(n);rd(m);
for (int i = 2; i < N; ++i)
lg[i] = lg[i / 2] + 1;
for (int i = 1; i <= n; ++i)
{
rd(f[i][0]);
}
for (int i = 1; i <= lg[n]; ++i)
{
for (int j = 1; j + (1 << i) - 1 <= n; ++j)
{
f[j][i] = max(f[j][i - 1], f[j + (1 << i - 1)][i - 1]);
}
}
int l, r;
while (m--)
{
rd(l);rd(r);
int s = lg[r - l + 1];
printf("%d\n",max(f[l][s], f[r - (1 << s) + 1][s]));
}
return 0;
}
练习(二维st表)
题目大意
给定一个二维数组,要求找到满足限制条件的最大正方形,
限制条件为:正方形内所有元素都不小于该正方形的边长。
解题思路
显然可以二分答案,解题的关键如何快速判断正方形是否满足该限制条件。
使用二维st表,来查询一个二位区间(方形)的最值。
参考代码
#include <bits/stdc++.h>
using namespace std;
int lg[1100];
void work()
{
int n, m;
cin >> n >> m;
int st[n][m][10];
int t;
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
cin >> t;
st[i][j][0] = t;
}
}
for (int k = 1; k <= lg[n]; ++k)
{
for (int i = 0; i + (1 << k) - 1 < n; ++i)
{
for (int j = 0; j + (1 << k) - 1 < m; ++j)
{
st[i][j][k] = min(min(st[i][j][k - 1], st[i + (1 << k - 1)][j + (1 << k - 1)][k - 1]),\
min(st[i + (1 << k - 1)][j][k - 1], st[i][j + (1 << k - 1)][k - 1]));
}
}
}
int l = 1, r = n, ans = 1;
while (l <= r)
{
int x = (l + r) / 2;
int s = lg[x];
int z = 0;
for (int i = 0; i + x - 1 < n; ++i)
{
for (int j = 0; j + x - 1 < m; ++j)
{
if (min(min(st[i][j][s], st[i + x - (1 << s)][j + x - (1 << s)][s]),\
min(st[i + x - (1 << s)][j][s], st[i][j + x - (1 << s)][s])) >= x)
{
z = 1;
}
}
}
if (z)
{
ans = x;
l = x + 1;
}
else
{
r = x - 1;
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T;
cin >> T;
for (int i = 2; i < 1100; ++i)
{
lg[i] = lg[i / 2] + 1;
}
while (T--)
{
work();
}
return 0;
}