ST 表模版
简介
给定一个长度为 $n$ 的数组,支持 $O(1)$ 查询区间最值。
使用前,调用 init()
函数初始化 ST,然后使用 query()
函数查询区间最值,下标范围从 $\left [0, n\right )$。
当前代码实现区间最小值问题,区间的最大值,最大公约数等均可以查询。
原理
预处理出原区间内长度为 $1, 2, 4, \cdots, 2^k$ 的子区间的答案,此预处理的复杂度为 $O(n \log n)$。
预处理的时,长度为 $1$ 的子区间信息直接从原数组中得到。长度大于 $1$ 为 $2^k$ 的子区间信息由两个长度为 $2^{k-1}$ 的区间合并得到。如下图中,表示 $[1, 8]$ 的橘红色区间,由两个长度为 $4$ 的灰色区间($[1,4]$, $[5,8]$)合并得到,黑色箭头表示合并操作。
查询的时候,先寻找区间覆盖查询区间的两个预处理长度为 $2^k$ 的区间,要求 $2^k$ 小于等于查询区间长度且最大,用从查询区间左侧起与右侧起的两个 $2^k$ 区间的答案组合出查询答案,单次查询复杂度 $O(1)$。
如下图,蓝色区间表示查询区间 $[3, 8]$,长度为 $6$,那么我们可以找到两个长度为 $4$ 的区间($[3, 6]$, $[5, 8]$)将其完全覆盖。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
namespace SparseTable{ const int MAXN=1e5+50; int lg2[MAXN]; int dp[MAXN][20]; void init(int v[], int n){ lg2[0]=-1; for (int i=1; i<MAXN; i++) lg2[i]=lg2[i>>1]+1; for (int i=0; i<n; i++) dp[i][0]=v[i]; for (int j=1; (1<<j)<n; ++j){ for (int i=0; i+(1<<(j-1))<n; i++){ dp[i][j]=min(dp[i][j-1], dp[i+(1<<(j-1))][j-1]); } } } int query(int l, int r){ if (l>r) return 1e9; int k=lg2[r-l+1]; return min(dp[l][k], dp[r-(1<<k)+1][k]); } } |

看坑神, 对这个st模块有点怀疑,
发现了这个网站
https://oi-wiki.org/ds/sparse-table/
我打周赛的时候也发现了,总是忘记改……[1,n]和[0, n)搞混了……现在改过来了!
我就是看你打周赛的视频,发现你对这个模板不太自信, 所以帖的。
另外,你的动态规划的文章写的很好,我学到不少。
ヾ(≧∇≦*)ゝ 吼的,谢谢提醒~