背景
我们在求区间最大最小值时,可以暴力从l到r,也可以用线段树、树状数组,但是都没有ST表的O(logn)建立,O(1)查询快
思路
对于一个数组,我们首先知道Max[1,1],Max[2,2],Max[3,3]…Max[n,n],是不是就进一步知道了Max[1,2],Max[2,3],Max[3,4]…,然后就可以知道Max[1,4],Max[2,5],Max[3,6],Max[4,7],Max[5,8]…然后就可以知道Max[1,8],Max[9,16]…
建表
ST表用的就是一个倍增思想,所以建表时logn的,从上面可以知道ST表建立的状态转移方程是:F[i][j] = max(F[i][j-1],F[i+(1 << j-1)][j-1]);
注意我们用到了log函数,他比较慢,所以我们预处理一下
void init() {
logn[0] = 0;
logn[1] = 0;
logn[2] = 1;
for(int i=3;i<=n;i++)
logn[i] = logn[i/2]+1;
}
void GetSt() {
for(int i=1;i<=logn[n];i++) {
for(int j=1;j+(1 << i)-1<=n;j++) {
f1[j][i] = max(f1[j][i-1],f1[j+(1 << i-1)][i-1]);
f2[j][i] = min(f2[j][i-1],f2[j+(1 << i-1)][i-1]);
}
}
}
查询
如果我们查找3-13,那么区间长度是11,但11有不是2的整数次幂,所以我们就要让查询的区间长度变成8,接着对3-10和6-13取max即可。两区间有交集,但是我们求的是最大值,允许有重复贡献
int findMin(int l,int r) {
int x = logn[r - l + 1];//区间长度!!!!!
return min(f2[l][x],f2[r - (1 << x) + 1][x]);
}
例题
2021牛客暑假多校(5)K: King of Range
题意:给一个数组,问有多少个子数组满足以下条件:最大值减最小值大于k
思路:我们先用ST表处理出最大值与最小值,然后用双指针从前往后扫描L,假如某个区间[L-R]满足条件,那么[L-R+1],[L-R+2]…[L-n]都会满足条件,所以答案res += n - r + 1即可,由于L从前往后,所以子数组不会重复
#include<iostream>
using namespace std;
#define inf 0x3f3f3f3f
#define IO ios::sync_with_stdio(false)
#define bug cout << "-----\n"
typedef long long ll;
int Mod = 1000000007;
const int N = 100010;
const int M = 500010;
int n;
int logn[N],f1[N][20],f2[N][20];
void init() {
logn[0] = 0;
logn[1] = 0;
logn[2] = 1;
for(int i=3;i<=n;i++)
logn[i] = logn[i/2]+1;
}
void GetSt() {
for(int i=1;i<=logn[n];i++) {
for(int j=1;j+(1 << i)-1<=n;j++) {
f1[j][i] = max(f1[j][i-1],f1[j+(1 << i-1)][i-1]);
f2[j][i] = min(f2[j][i-1],f2[j+(1 << i-1)][i-1]);
}
}
}
int findMin(int l,int r) {
int x = logn[r - l + 1];//区间长度!!!!!
return min(f2[l][x],f2[r - (1 << x) + 1][x]);
}
int findMax(int l,int r) {
int x = logn[r - l + 1];
return max(f1[l][x],f1[r - (1 << x) + 1][x]);
}
int main() {
int m,i,j,k;
cin >> n >> m;
init();
for(i=1;i<=n;i++){
cin >> f1[i][0];
f2[i][0] = f1[i][0];
}
GetSt();
while(m--) {
ll res = 0;
cin >> k;
int l = 1,r = 1;
while(l<=r&&r<=n) {
if(findMax(l,r) - findMin(l,r) > k) {
res += n - r + 1;
l ++;
}
else {
r ++;
}
if(l == r)
r ++;
}
cout << res << endl;
}
return 0;
}