ST(Sparse Table) 算法
简介
-
适用范围
ST 表是用于解决可重复贡献问题的的区间询问的数据结构,基本思想是倍增和DP。
所谓可重复贡献问题,即运算具有自反性的问题,例如求最大/小值,最大公约数,最小公倍数 x = m a x ( x , x ) = m i n ( x , x ) = g c d ( x , x ) = l c m ( x , x ) x=max(x,x)=min(x,x)=gcd(x,x)=lcm(x,x) x=max(x,x)=min(x,x)=gcd(x,x)=lcm(x,x)(都可归为RMQ(Range Minimum/Maximum Query)问题)等,而像求和就不具有自反性,所以不能用ST表解决。 -
ST算法基本描述
通过状态转移方程预处理答案后,对于每个询问的区间将其拆分成两个叠加后将询问区间刚好完全覆盖的区间,然后取两个区间的运算结果作为询问区间的结果,所拆分成的两个区间可以有重叠部分(由于处理的是可重复贡献问题,所以没有影响)。 -
具体实现
1、用一个二维数组 S T [ i ] [ j ] ST[i][j] ST[i][j]表示区间 【 i , i + 2 J − 1 】 【i,i+2^J-1】 【i,i+2J−1】的答案。
2、根据状态转移方程,预处理出ST数组,即 S T [ i ] [ j ] = o p t ( S T [ i ] [ , j − 1 ] , S T [ i + 2 J − 1 ] [ j − 1 ] ) ST[i][j]=opt(ST[i][,j-1],ST[i+2^{J-1}][j-1]) ST[i][j]=opt(ST[i][,j−1],ST[i+2J−1][j−1])
3、对于询问区间 【 l , r 】 【l,r】 【l,r】,则其答案 a n s = o p t ( S T [ l ] [ l + 2 s ] , S T [ r − 2 s + 1 ] [ r ] ) ans=opt(ST[l][l+2^s] , ST[r-2^s+1][r]) ans=opt(ST[l][l+2s],ST[r−2s+1][r]),其中 s = l o g 2 ( r − l + 1 ) s=log_2(r-l+1) s=log2(r−l+1)。
为什么是拆分成这个两个区间,因为这两个区间合并后一定刚好完全覆盖询问区间。 -
复杂度分析
需要 O ( n l o g n ) O(nlogn) O(nlogn)的时间复杂度和空间复杂度进行需处理,然后对于每个询问只需要 O ( 1 ) O(1) O(1)的复杂度获得答案。所以总的复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)。
例题
例题一:洛谷3865
模板题。
#include <bits/stdc++.h>
using namespace std;
const int logn = 22;
const int maxn = 2000005;
int f[maxn][logn], Logn[maxn];
int n, m, x, y;
void pre()
{
//预处理log函数
Logn[1] = 0;Logn[2] = 1;
for (int i = 3; i < maxn; i++)
Logn[i] = Logn[i >> 1] + 1;
//递推
for (int j = 1; j <= logn; 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]);
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> f[i][0];
pre();
for (int i = 1; i <= m; i++)
{
cin >> x >> y;
int s = Logn[y - x + 1];
int ans=max(f[x][s], f[y - (1 << s) + 1][s]);
cout<<ans<<'\n';
}
return 0;
}
例题二:毒瘤题
这题我调了几个小时也没调出来,主要是他的判定有点恶心,希望有同学做出来以后给我发一下。
简单说一下思路:开两个ST数组,
S
T
M
I
N
STMIN
STMIN和
S
T
M
A
X
STMAX
STMAX,一个存最大值,一个存最小值。将数组初始化为-1,完成输入后预处理出两个ST数组。由于初始化是-1,所以
S
T
M
I
N
[
i
]
[
j
]
=
=
−
1
STMIN[i][j]==-1
STMIN[i][j]==−1则表示该区间有未知的年份。然后根据题意输出结果即可。