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表)

D. Valiant's New Map

题目大意
给定一个二维数组,要求找到满足限制条件的最大正方形,
限制条件为:正方形内所有元素都不小于该正方形的边长。

解题思路
显然可以二分答案,解题的关键如何快速判断正方形是否满足该限制条件。
使用二维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;
}
posted @ 2023-01-03 12:17  何太狼  阅读(185)  评论(0编辑  收藏  举报