st 表的学习总结

· · 个人记录

基础概念

需以 O(nlogn) 的时间复杂度构造 st 表。

可以用 O(1) 的时间复杂度,查询区间最大、最小值。但广义地说,满足结合律即可,如 gcd、异或等。

st 表是静态的,难以再修改。

st 表 vs 线段树

线段树的优势是:可以动态修改。 ### 实现 首先,$st$ 表是用动态规划实现的,用到了倍增的思想。 定义 $st[i][j]$ 表示:一段以 $i$ 为起点,长度为 $2^j$ 的区间的最值。 容易推出转移方程:$st[i][j]=max(st[i][j-1],st[i+2^{j-1}][j-1])$ 。 怎么查询?不妨设 $len=log2(r-l+1)$,向下取整,用两段长为 $len$ 区间覆盖即可:$max(st[l][len],st[r-2^{len}+1][len])$。 ### 代码 [模板题](https://www.luogu.com.cn/problem/P3865) ```cpp #include<bits/stdc++.h> using namespace std; int n,m,a[100005],d[100005],l,r,len,f[100005][30]; void st() { for(int j=0; j<=25;j++) for(int i=1; i+(1<<j)-1<=n;i++) { if(j==0) f[i][j]=a[i]; else f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]); } } int main(){ cin>>n>>m; for(int i=1; i<=n;i++) { scanf("%d",&a[i]); if(i>=2) d[i]=d[i>>1]+1; } st(); for(int i=1; i<=m;i++) { scanf("%d%d",&l,&r); len=d[r-l+1]; printf("%d\n",max(f[l][len],f[r-(1<<len)+1][len])); } return 0; } ``` ### 几道好题 #### P1198 [JSOI2008] 最大数 [链接](https://www.luogu.com.cn/problem/P1198) 一开始看到:这不是单调队列模板吗? 后来由空线段树的思想,联想到边读边建表,过了。 #### P7244 章节划分 [链接](https://www.luogu.com.cn/problem/P7244) 很好的一道题。 首先想到:答案一定是最大数的因子。 于是可以对最大数进行分解质因数,枚举每个因子:把它作为答案是否可行?就转化为了判定问题。 可求出在满足条件的情况下,区间最多被分为几段。因为,假如最多分为 $k$ 段,那么可以把其中两段合在一起,变为 $k-1$ 段,还是满足条件,以此类推。 下面给出一种巧妙的递归写法: 要求 $(l,r)$ 这段区间至多可被为多少段,不妨设 $ma$ 为区间最大值。 那么可分为两种情况: 1. $ma$ 整除答案,那为了使段数最多,不妨把 $ma$ 独立出来,则:$(l,r)=(l,ma-1)+(ma+1,r)+1$ 。 2. 不整除,考虑把 $ma$ 纳入它上面一级的区间,因为上一级一定满足条件。因此:若 $l>1$,不用考虑 $l$ 到 $ma$ 这段区间 ($l<1$ 的话没法覆盖住 $ma$)。若 $r<n$,同理。 #### P7009 [CERC2013] Magical GCD [链接](https://www.luogu.com.cn/problem/P7009) 显而易见,$st$ 表可求出区间 $gcd$ 的值。 上手枚举下 $gcd$ 的值,可发现:一段区间,左端点固定,$gcd$ 的值随着右端点右移而单调不增。 由于题目时限为 $8s$,很容易想出一种暴枚的做法。 固定区间左端点,用二分求出 $gcd$ 值相等时,右端点最远在哪,然后统计最大值即可。 #### P2048 [NOI2010] 超级钢琴 [链接](https://www.luogu.com.cn/problem/P2048) 题目要求 $k$ 段序列最大值,贪心地想,就是求 $k$ 次最大值。 那不妨固定住区间左端点 $i$,对于每个 $1≤i≤n$,都能找到使该区间和最大的 $j$,把它们存入堆。我用的是优先队列 + 重载运算符的做法,因为相对简单。 然后要用到前缀和,每个区间可表示为 $s[j]-s[i]$ 的形式,既然要求最大且已知 $s[i]$ 的值,那么只需找到最大的 $s[j]$,可以用 $st$ 表预处理。 接着循环 $k$ 次,每次取最大的区间,把它拆分为次小区间(最多 $2$ 个),因为之后可能取到它们。 那么如何拆分?设 $l$、$r$ 分别最大区间左右端点,我们已知 $ma$ 的值,使得 $s[ma]-s[i]$ 的值最大,因此次大 $m_1$ 一定在 $l$ 到 $ma-1$ 之间;当然,前提是 $l<ma$。$m_2$ 同理。