//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

ST表

ST表

引入

给定 \(n\) 个数,有 \(m\) 个询问,对于每个询问,求出 \([l,r]\) 中的最大值。

我们都会暴力,直接枚举取 \(\max\),但是复杂度最坏是 \(O(n^2)\) 的,我们需要更优的做法。

思想

ST 表基于倍增思想,可以做到 \(O(n\log n)\) 的预处理,然后 \(O(1)\) 查询,总复杂度为 \(O(n\log n + m)\)

我们如果是和倍增一样,每次跳 \(2^i\) 步的话,我们询问的复杂度是 \(O(\log n)\) 的,并没有比线段树优,由于预处理复杂度还不如线段树。

我们设 \(f_{i,j}\) 表示区间 \([i,i + 2^j - 1]\) 的最大值。

显然 \(f_{i,j} = a_i\)

我们通过倍增的思路,写出下面的转移方程:

\[f_{i,j} = \max(f_{i,j - 1}, f_{i + 2^{j-1},j-1}) \]

image

然后就可以用 \(O(n\log n)\) 的复杂度进行预处理了。

对于每一个查询操作,我们把 \([l,r]\) 拆成两个询问,\([l,l + 2^s -1]\)\([r - s^2 + 1, r]\),其中 $s = \left \lfloor \log_{2}(r- l +1) \right \rfloor $。这两个部分的较大值就是答案。

image

【模板】ST 表 - 洛谷

code:

#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define endl '\n'

using namespace std;

int n, m, a[N], f[N][21];

inline int read(){int x=0,f=1;char ch=getchar();while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}return x*f;}

inline int ask(int l, int r)
{
    int k = log2(r - l + 1);
    return max(f[l][k], f[r - (1 << k) + 1][k]);
}

signed main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i ++) f[i][0] = read();
    for(int j = 1; j <= 20; j ++)
        for(int i = 1; i + (1 << j) - 1 <= n; i ++)
            f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    for(int i = 1; i <= m; i ++)
    {
        int l = read(), r = read();
        cout << ask(l, r) << endl;
    }
    return 0;
}

练习题

https://loj.ac/problem/2279

[USACO07JAN] Balanced Lineup G - 洛谷

P8818 [CSP-S 2022] 策略游戏

这个题目很不错,有空来补题解。

/*
 * @Author: Aisaka_Taiga
 * @Date: 2023-09-04 08:38:52
 * @LastEditTime: 2023-09-04 09:52:05
 * @LastEditors: Aisaka_Taiga
 * @FilePath: \Desktop\P8818.cpp
 * 心比天高,命比纸薄。
 */
#include <bits/stdc++.h>

#define INF LONG_LONG_MAX
#define int long long
#define N 1000100

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}
//1是A最大值,2是A最小值,3是B最大值,4是B最小值
//5是A负数最大值,6是A非负最小值
int n, m, q, st[7][N][21], ans;
int a[N], b[N];

inline int ask(int l, int r, int p)
{
    int k = log2(r - l + 1);
    if(p % 2 == 1) return max(st[p][l][k], st[p][r - (1 << k) + 1][k]);
    else return min(st[p][l][k], st[p][r - (1 << k) + 1][k]);
}

signed main()
{
    n = read(), m = read(), q = read();
    int up = max(n, m);
    for(int i = 1; i <= n; i ++) a[i] = read();
    for(int i = 1; i <= m; i ++) b[i] = read();
    for(int i = 1; i <= up; i ++)
    {
        st[1][i][0] = st[2][i][0] = a[i];
        st[3][i][0] = st[4][i][0] = b[i];
        if(a[i] >= 0) st[5][i][0] = -INF, st[6][i][0] = a[i];
        else st[5][i][0] = a[i], st[6][i][0] = INF;
    }
    for(int j = 1; j <= 19; j ++)
    {
        for(int i = 1; i <= up; i ++)
        {
            st[1][i][j] = max(st[1][i][j - 1], st[1][i + (1 << (j - 1))][j - 1]);
            st[2][i][j] = min(st[2][i][j - 1], st[2][i + (1 << (j - 1))][j - 1]);
            st[3][i][j] = max(st[3][i][j - 1], st[3][i + (1 << (j - 1))][j - 1]);
            st[4][i][j] = min(st[4][i][j - 1], st[4][i + (1 << (j - 1))][j - 1]);
            st[5][i][j] = max(st[5][i][j - 1], st[5][i + (1 << (j - 1))][j - 1]);
            st[6][i][j] = min(st[6][i][j - 1], st[6][i + (1 << (j - 1))][j - 1]);
        }
        // cout << " J : " << j << endl;
    }
    while(q --)
    {
        int l1 = read(), r1 = read(), l2 = read(), r2 = read(), ans = -INF;
        int c1 = ask(l1, r1, 1), c2 = ask(l1, r1, 2);
        int c3 = ask(l2, r2, 3), c4 = ask(l2, r2, 4);
        int c5 = ask(l1, r1, 5), c6 = ask(l1, r1, 6);
        ans = max(ans, c1 * (c1 >= 0 ? c4 : c3));
        ans = max(ans, c2 * (c2 >= 0 ? c4 : c3));
        if(c5 != -INF) ans = max(ans, c5 * (c5 >= 0 ? c4 : c3));
        if(c6 != INF) ans = max(ans, c6 * (c6 >= 0 ? c4 : c3));
        cout << ans << endl;
    }
    return 0;
}

参考自OIwiki。

posted @ 2023-08-19 10:19  北烛青澜  阅读(5)  评论(0编辑  收藏  举报