ST表及其应用

背景

我们在求区间最大最小值时,可以暴力从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;
}
上一篇:C语言常用函数-access()文件访问权限设置函数


下一篇:多态