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})
\]
然后就可以用 \(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 $。这两个部分的较大值就是答案。
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;
}
练习题
[USACO07JAN] Balanced Lineup G - 洛谷
这个题目很不错,有空来补题解。
/*
* @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。
本文来自博客园,作者:北烛青澜,转载请注明原文链接:https://www.cnblogs.com/Multitree/p/17642128.html
The heart is higher than the sky, and life is thinner than paper.