ST表
什么是ST表
ST表是一种神奇的数据结构,它虽说有它的短板——不支持修改,但它的特点同样很鲜明——短小精悍,能做到 的预处理,
的单次查询。
实现方法:
ST表分为预处理和区间查询两个部分,实现起来十分简单。
预处理的预处理:
由于我们需要每次倍增的运算,所以一定会涉及到多次计算
和
,所以可以对这两个数组进行预处理。
实现方案十分简单,就直接放代码了
num[0] = 1,lg[0] = -1; for(int i = 1 ; i <= 30 ; i++) num[i] = num[i - 1] * 2; for(int i = 1 ; i <= n ; i++) lg[i] = lg[i / 2] + 1;
真正的预处理:
设
表示从第i号节点到第
号节点的最大值,即从
号节点开始往后数共
个节点中的最大值。
例如指的就是第
号节点和第
号节点 的最大值。
但是暴力的来构造肯定是不行的呀,我们考虑利用性质进行二分。我们可以逐层循环j和i,当我们循环到要处理f[i][j]时,我们其实就是要求这个区间到
中的最大值。
这时我们发现,由于区间长度为2^j,所以是可以分割成左右两部分的,即到
和
到
所以我们原区间的最大值就变成了两个区间的最大值的最大值,代码如下:
for(int i = 1 ; i <= lg[n] ; i++) { for(int j = 1 ; j <= n ; j++) { if(j + num[i] - 1 <= n) f[j][i] = max(f[j][i - 1],f[j + num[i - 1]][i - 1]); } }
区间查询:
首先,先说一个定理:
。
这个定理告诉我们,任意一个长度为的区间,都能被两端长度为
的区间所覆盖 。
所以我们的查询也变得很简单了,假如我们要查询区间的最大值,很显然,这段区间长度
。
那么我们只要求出以为首,长度为
的区间的最大值和以
为尾,长度为
的区间的最大值,就一定包括了整个区间的最大值。
所以我们可以知道中的最大值为
。
例题:luogu P3865
题意:
给你一个长度为
的的数列和
组询问,询问序列中的最大值。
CODE:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; #define LL long long const int N = 1e6 + 100; int f[N][30],num[N]; int m,n,ans,lg[N]; int main() { scanf("%d%d",&n,&m); for(int i = 1 ; i <= n ; i++) scanf("%d",&f[i][0]); num[0] = 1,lg[0] = -1; for(int i = 1 ; i <= 30 ; i++) num[i] = num[i - 1] * 2; for(int i = 1 ; i <= n ; i++) lg[i] = lg[i / 2] + 1; for(int i = 1 ; i <= lg[n] ; i++) { for(int j = 1 ; j <= n ; j++) { if(j + num[i] - 1 <= n) f[j][i] = max(f[j][i - 1],f[j + num[i - 1]][i - 1]); } } for(int i = 1 ; i <= m ; i++) { int l,r; scanf("%d%d",&l,&r); int cnt = lg[r - l + 1]; ans = max(f[l][cnt],f[r - num[cnt] + 1][cnt]); printf("%d \n",ans); } //system("pause"); return 0; }