重学ST表
ST表能做什么?
- 预处理并 求出区间最大/最小值(也就是 RMQ, Range Maximum/Minimum Query )。
- 区间 GCD 问题。
- 区间按位和/或。
而以上这些,都有一个特性:一个值对于好几段包含自己的区间都会有贡献。这一类题大多都可以用ST表解决。
ST表的实现?
我们以 P3865 【模板】ST 表 为例,讲述一下ST表是如何实现 地求出静态区间最大值的。
原题样例:
9 3 1 7 5 6 0 8推导方程
我们令 表示原序列中第 个数字起(含)往后 个,也就是原数组 到 (两边都含)的区间最大值。显然,对于单个的 的区间的最大值就是 本身。而这一段区间的长度为 。所以我们可以得到 。
然后我们继续初始化st表。我们要先定下来区间长度 ,再枚举第 个数(为什么要这样的顺序呢?请往下看)。现在我们想要得出 ,也就是 到 的区间最大值,应该由哪些 转移过来呢?答案是 和 。为什么呢?
对于一个区间,如果它的长度为 ,那么它就由两个长度为 的区间得来(因为区间为 的 早就处理好了)。长度为 ,是由两个长度为 的区间得来的。以此类推,我们发现只要把当前区间对半分,就能推导出当前区间的值了。原因很简单,我们的st表的第二位是 ,这也决定了它是由一个一个的 累积起来的结果。
我们已经知道了上一个区间的长度是 ,也就是 数组的第二维是 。下面我们来推导第一维是几。例如,对于 这个区间,两个子区间是 和 。可以知道较大的那个区间的左端点是 ,这是大区间的左端点加上区间长度的一半得到的,也就是 。由于st表只需要知道左端点和区间长度即可,那么两个 分别是 和 。
注意, ,这应该不难理解。下面是对于每个 转移的代码:
st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);现在也解答了两层循环遍历顺序的问题。假使我们一定要先枚举第一维 ,对于每一个 计算它在某个区间内的贡献。然而这个区间是往后的,也就是说, 以后的 全没有处理好,也就无法递推了。
循环条件
- 第一层循环:枚举 ,范围: ,即 (由于 ,就是长度为 的情况已经得知了,不用再来一遍)。
- 第二层循环:枚举 ,即考虑当前 对于 所规定的区间的贡献。由于长度为 ,所以 且 。刚才不是说长度为 吗?那为什么不是 呢?因为st表要把 中末尾几个数字也要考虑进去。那么我们只需满足推导方程中数组中的条件 即可。
以上理解似乎有些问题,有待改进。
区间查询
现在已经完成了预处理,要来进行查询了。给定区间 ,要求其中的最大值。如果这个 恰好是 中已有的区间,那最好了,直接输出。若 的长度恰好不是 的整区间呢?那就要把这个区间再分成两块。可是,即使是这样也不能保证这两个被分出来的区间的长度是 。既然我们不能恰好分,那就让这两个区间有所重叠吧。于是我们得到了查询函数:
int solve(int l,int r){ return max(st[l][(int)log2(r-l+1)],st[r-(1<<int(log2(r-l+1)))+1][(int)log2(r-l+1)]);}这里应该不难理解。下面给出完整代码。
P3865 AC Code
#include <bits/stdc++.h>using namespace std;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;}const int maxn=1e6+5;int n,m;int a[maxn];int st[maxn][(int)log2(maxn)+1];int l,r;int solve(int l,int r){ return max(st[l][(int)log2(r-l+1)],st[r-(1<<int(log2(r-l+1)))+1][(int)log2(r-l+1)]);}int main(){ n=read(),m=read(); for(int i=1;i<=n;i++){ a[i]=read(); st[i][0]=a[i]; } for(int j=1;j<=log2(n);j++){ for(int i=1;i+(1<<(j-1))<=n;i++){ st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } while(m--){ l=read(),r=read(); cout<<solve(l,r)<<"\n"; } return 0;}支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
无穷大?