ST表

\(ST\)

引入:

\(ST\) 表是用于解决可重复贡献问题的数据结构。

​ 可重复贡献问题 是指对于运算 \(opt\) ,满足 \(x opt x=x\) ,则对应的区间询问就是一个可重复贡献问题。

例如,最大值有 \(max(x,x)=x\)\(gcd\)\(gcd(x,x)=x\) ,所以 \(RMQ\) 和区间 \(GCD\) 就是一个可重复贡献问题。

像区间和就不具有这个性质,如果求区间和的时候采用的预处理区间重叠了,则会导致重叠部分被计算两次.

另外,\(opt\) 还必须满足结合律才能使用 \(ST\) 表求解。

例题:

\(ST\) 表基于 倍增 思想,可以做到 \(O(nlogn)\) 预处理, \(O(1)\) 回答每个询问。但是不支持修改操作。

基于倍增思想,我们考虑如何求出区间最大值。可以发现,如果按照一般的倍增流程,每次跳 \(2^i\) 步的话,询问时的复杂度仍旧是 \(O(logn)\) ,并没有比线段树更优,反而预处理一步还比线段树慢。

我们发现 \(max(x,x)=x\) ,也就是说,区间最大值是一个具有“可重复贡献”性质的问题。即使用来求解的预处理区间有重叠部分,只要这些区间的并是所求的区间,最终计算出的答案就是正确的。

如果手动模拟一下,可以发现我们能使用至多两个预处理过的区间来覆盖询问区间,也就是说询问时的时间复杂度可以被降至 \(O(1)\) ,在处理有大量询问的题目时十分有效。

具体实现如下:

\(f(i,j)\) 表示区间 \([i,i+2^{j−1}]\) 的最大值。

显然 \(f(i,0)=a_i\)

根据定义式,第二维就相当于倍增的时候“跳了 \(2^{j−1}\) 步”,依据倍增的思路,写出状态转移方程:

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

以上就是预处理部分。而对于查询,可以简单实现如下:

对于每个询问 \([l,r]\) ,我们把它分成两部分: \(f[l,l+2^{s}]\)\(f[r−2^{s}+1,r]\)

其中 \(s=⌊log_2(r−l+1)⌋\)

由于最大值是“可重复贡献问题”,重叠并不会对区间最大值产生影响。又因为这两个区间完全覆盖了 \([l,r]\) ,可以保证答案的正确性。

模板代码:

#include<bits/stdc++.h>
using namespace std;
int f[100005][20];
int n,m;
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&f[i][0]);
    for(int j=1;j<=floor(log2(n));j++)
    	for(int i=1;i<=n+1-(1<<j);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,r;
        scanf("%d%d",&l,&r);
        int k=floor(log2(r-l+1));
        printf("%d\n",max(f[l][k],f[r+1-(1<<k)][k]));
    }
    return 0;
}

小结:

稀疏表 \((SparseTable)\) 算法是 \(O(nlogn)−O(1)\) 的,对于查询很多大的情况下比较好。

\(ST\) 算法预处理:用 \(dp[i,j]\) 表示从 \(i\) 开始的,长度为 \(2^j\) 的区间的 \(RMQ\) ,则有递推式

\[dp[i,j]=\min (dp[i,j−1],dp[i+2^{j-1}]) \]

即用两个相邻的长度为 \(2^{j−1}\) 的块,更新长度为 \(2^{j}\) 的块。因此,预处理时间复杂度为 \(O(nlogn)\)

这个算法记录了所有长度形如 \(2^k\) 的所有询问的结果。

从这里可以看出,稀疏表算法的空间复杂度为 \(O(nlogn)\)

扩展:

\(RMQ\) 以外,还有其它的“可重复贡献问题”。例如“区间按位和”、“区间按位或”、“区间 \(GCD\)”,\(ST\) 表都能高效地解决。

需要注意的是,对于“区间 \(GCD\)”,\(ST\) 表的查询复杂度并没有比线段树更优(令值域为 \(w\)\(ST\) 表的查询复杂度为 \(Θ(logw)\) ,而线段树为 \(Θ(logn+logw)\) ,且值域一般是大于 \(n\) 的),但是 \(ST\) 表的预处理复杂度也没有比线段树更劣,而编程复杂度方面 \(ST\) 表比线段树简单很多。

posted @ 2021-09-28 17:08  Evitagen  阅读(72)  评论(0编辑  收藏  举报