C++高级数据结构——ST表(倍增表)

10 篇文章 3 订阅
4 篇文章 1 订阅

 从上次写文章到现在已经27天了,将近一个月蒟蒻没有更新了。

最近学的ST表太难理解了,再加上忙,一直没时间……

---------------------------------------------------------------------------------------------------------------------------------

开始之前,请各位(尤其蒟蒻,大佬绕开这里)做好心理准备,有可能你真的会看1~2遍才能懂,因为本蒟蒻就是这样……

正文开始

今天我们所讲的叫做ST表,也被称为倍增表。

ST表一般来说是用来处理“区间最值问题”(RMQ问题),就比如一个区间内的最大值、最小值之类的。

一提到区间最值问题,第一反应肯定会想到直接for循环枚举,但这种时间复杂度实在高,如果有m次询问,那么时间就得达到大约O(mn) (蒟蒻算时间复杂度很垃圾,也许不对:( ),但使用ST表,时间就会骤降到O(nlogn),其中,每次询问只是O(1)的时间,太猛了简直。

思想:

ST表采用动态规划的思想,但是他表达状态的方式不太一样。(不要问发明的人怎么想到的,记就完了)

我们先假设求区间最大值

定义f[i][j]表示从i开始,向后2^j个数这个区间内的最大值。那么我们看下图:

注:个数为(尾-头+1),所以i+2^j-1-i+1=2^j 

上图为整体,我们把这个整体分成两部分

 我们既然分为两个部分了,那么我们最后的那个结果,不管绿的部分的右端点在哪儿,或者红部分的左端点在哪儿,我们的结果都不发生改变。所以要极端一点:假设以下部分:

i+2^j-1=r(其中r为我们所求全部区间的右端点)

r-2^j+1=l(l为我们所求全部区间的左端点)

最后求出来,j=log2(r-l+1) 请没学过log的同学自行补习,比如本蒟蒻:(((

那么整个区间的最大值,就是两部分最大值再次进行比较。

得出状态转移方程:

f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])

思路差不多就讲完了,下面来看看模板代码:

在难懂的地方我会有注释,实在不懂私信蒟蒻()

scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++){
	scanf("%lld",&f[i][0]);
}
for (int j=1;(1<<j)<=n;j++){//枚举那个指数,运用位运算
	for (int i=1;i<=n-(1<<j)+1;i++){//枚举所有剩下的i的情况
		f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//转移方程,里面还是使用位运算,因为一个字,快
	}
}
for (int i=1;i<=m;i++){
	int l,r;
	scanf("%lld%lld",&l,&r);
	int s=log2(r-l+1);//反求一下指数
	printf("%lld\n",max(f[l][s],f[r-(1<<s)+1][s]));//两个区间取最大值
}

例题:

纯纯大模板

给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。

输入

第一行包含两个整数 N,M,分别表示数列的长度和询问的个数。

第二行包含 N 个整数(记为 ai),依次表示数列的第 i 项。

接下来 M 行,每行包含两个整数 li,ri,表示查询的区间为 [li,ri]。

输出

输出包含 M 行,每行一个整数,依次表示每一次询问的结果。

样例输入1
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8
样例输出1
9
9
7
7
9
8
7
9

希望各位同学在自己理解的基础上,尽量独立完成

分析优缺点:
 

优点:时间太猛辣!空间太猛辣!

缺点:不能修改任何的值,除非使用线段树

今天对于ST表的讲解就到这里,大家下期再见!

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值