ST表的原理及代码实现

ST表是一种区间查找最大值的数据结构,它虽说有的短板——不支持修改,但它的特点比较牛逼,能做到O(nlogn)的预处理,O(1)的单次查询,其思路其实就是我们所熟悉的倍增思想。

基本思路:
我们现在用O(1)的复杂度求区间最大值,那么会有一个简单的思路,令f(i,j),为区间[i,j]的最大值,但这样的话预处理复杂度达到O(n^2),复杂度太大。考虑优化,有一个关键性质:max操作允许区间重叠,也就是max(a,b,c)=max(max(a,b),max(b,c))
这里我们采用倍增思想,令f(i,j)为a[i]开始的数,表示第i号节点,到第i+2^j-1号节点,连续j ^ 2个的区间的最大值。
f(i,j) = max(f(i,j-1),f(i+2^(j-1),j-1))
初始化时,每一个状态对应的区间长度都为2^ j,由于给出的查询区间长度不一定恰好为2^ j。
现在还需要引出一个定理:2^log(x)>x/2,任意一个长度为x的区间,都能被两端长度为2^log(x)的区间所覆盖。
所以我们的查询也变得很简单了,假如我们要查询区间[x,y]的最大值,很显然,这段区间长度t=y-x+1;那么我们只要求出以x为首,长度为2 ^ log(t)的区间的最大值 和 以y为尾,长度为2 ^log(t)的区间的最大值,就一定包括了整个区间的最大值。
max(f(x,log t),f(y-2^log t+1,log t))
代码实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stdio.h>
using namespace std;
long f[100010][30] = { 0 }; //st表
long a[100010];//输入数组
int main()
{
	long int n;
	//cin >> n;
	long int m;
	//cin >> m;
	scanf("%ld %ld", &n, &m);
	for (long i = 1; i <= n; i++)
	{
		long c;
		scanf("%ld", &c);
		f[i][0] = c;
		//cin >> a[i];
		//f[i][0] = a[i];//实际上可直接输入到ST表内
	}
	for (int j = 1; j <= log2(n) + 1; j++)
	{
		for (long int i = 1; i + (1 << j)-1 <= n; i++)
		{
			f[i][j] = max(f[i][j-1], f[i+(1<<(j-1))][j-1]);
			//1<<(j-1)相当于2^(j-1),详情查看位运算
		}
	}
	//cin >> m;//查询次数
	for (long int i = 1; i <= m; i++)
	{
	long int x, y;//查找区间[x,y]中最大的数字
	scanf("%ld %ld", &x, &y);
	long int t = y - x + 1;
	 int lg = (int)log2(t);
	 printf("%ld\n",max(f[x][lg], f[y-(1<<lg)+1][lg]));
	//cout << max(f[x][lg], f[y-(1<<lg)+1][lg]) << endl;
	}
}

提示:cin,cout,要比printf,scanf慢,所以在卡时间的情况下使用后者!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值