st表是什么?怎么实现?

写在前面

这篇文章两年前就写的差不多了,说到底鼠鼠还是条懒狗,还没写完就一直烂在草稿里吃灰。

岁月不饶人啊,两年后回来看自己写的东西还不禁感慨,卧槽,以前的我怎么这么牛逼,感慨的是现在好像被磨平了,再也懒得玩梗,也再也没有之前的创造力和想象力了。

检验一个教程成不成功的办法就是小白能不能学明白,如今功力尽失的我照着自己的教程学习了一遍,发现两年前认为写小故事可以方便理解的想法还是太天真了,我的水平只能写的又臭又长,翻好久才能找到自己想学的干货,建议直接点击目录的找干货请跳到这里部分,

无聊的故事

在一个0和1组成的 (两年前这句话还没这么怪吧) ,充斥着剑与魔法的世界里,有坏事做尽的恶龙,有保卫王国的勇者,也有三天两头就被掳走的公主。 这里祥和安宁,夜幕降临时,在挂满灯笼的集市里踱步,听几声吆喝,颇有种大唐盛世的感觉。

*又是美好的一天,鸟儿在歌唱,花儿在绽放。*新手村里传来捷报,公主又双叒叕被抓走了。

就像西方不能失去耶路撒冷,这个世界也不能缺少勇者。阿李、阿黄、阿刘三人历经重重困难,终于达到了关押公主的城堡。

为了解救公主,他们必须打败城堡中的所有恶魔。每个勇者都会先战胜最弱的恶魔,提升自己的等级,从而更好地面对更强大的对手。。

“把所有恶魔全部欧拉一边就行了”, 阿李是一位狂战士,在他莽夫的策略路过的狗都得埃两巴掌,没想到转角就遇到战斗力最高的恶魔,至今下落不明。这种行为我们称之为暴力。

阿刘是个大聪明,他把城堡中每个区域最fw的恶魔都记到一个本子上,每次只要看一眼,就能找到战斗力最低的恶魔,结果繁杂的准备工作彻底击垮了这个成年人的心智。这种行为我们称之为打表。

阿黄是个魔法师,他拥有一面魔镜,进入城堡之前先对着镜子吟唱咒语“阿米诺斯!”,魔镜就飞起来了,以后只要对它说“一得个拉米!魔镜魔镜告诉我,城堡里这一块最弱的恶魔是谁”,镜子上就会出现这个范围里最弱的恶魔。毕竟是新手村买来的魔镜,如果有新的恶魔来了,镜子就白飞了

就像落叶忘不了秋风的温柔,多年后,阿黄成了这个世界数一数二的魔法师,荣归故里,衣锦还乡。他回到了那条灯火通明的街,和街坊邻居们吹嘘起自己的冒险之旅,让人仿佛感受到了那个时光的英勇。

”阿黄,给我们讲讲你当年救出公主那面镜子的故事吧…“

阿黄的镜子

题目链接

Input
输入第一行:一个整数 n (0<n<1e5) 表示城堡中恶魔的数量
接下来一行:n个整数 a[i] 第i个恶魔的战斗力,(0<a[i]<100)
接下来一行:一个整数 t(0<t<1e5)代表询问的次数
接下来t行:两个整数L,R,请你告诉阿黄第L到R个恶魔中最低战斗力是多少 (0<L<R<=n)

Output
对于每次询问输出一行 ,一个整数表示区间内恶魔的最低战斗力

样例输入 :
5
5 4 3 2 1
2
1 5
3 4
样例输出:
1
2

俗话说得好,没有耕坏的地,只有累死的牛,由于敌人数量高达1e5,每次查询都像某狂战士那样暴力求解显然是不可取的。聪明的勇者一定发现了,阿黄镜子的秘密就在于st表

找干货请跳到这里

ST表及其实现

ST表(Sparse Table,稀疏表)是一种简单的数据结构,主要用来解决RMQ(Range Maximum/Minimum Query,区间最大/最小值查询)问题。
说人话就是st表可以帮我们查询某个区间内的最值

吟唱咒语:“阿米诺斯!”,就是复杂度O(nlogn)的预处理操作,这里采用了倍增思想:
利用stmin[i][j]数组储存从i号开始,长度为2j的区间中的最小的数,这一点十分重要
比如stmin[1][3]代表从1开始长度为23=8的区间,也就是1-8号的最小值
每次查询只要O(1)的复杂度就会给出答案,所以不会像阿刘那样耻辱下播。

现在,我们有了以任意一点为起点长度为2k区间的最小值

如果查询1-5的最小值怎么办?对于每次查询,显然区间长度不能保证是2k

stmin[1][2]长度为4, 查询的是1-4的最小值
stmin[1][3]长度为8, 查询的是1-8的最小值

所以我们要把这个区间一分为二,寻找一个最小的k满足从两边端点为起点,相向而行且长度为2^k的两个区间,使得这两个区间的并集正好是1-5,如图所示,当k=2时,这两个区间的最小值就是答案了。

请添加图片描述
这个k值也很好求,就是log2(区间长度) 向下取整,此时2k一定保证和区间长度贴的很近甚至相等

说人话就是找两个长度为2k的区间,一个在左一个在右而且这两个区间并起来是要求的区间,把最小值夹出来

用lg[i]数组储存以2为底i的对数 (向下取整),即:
lg[1] =0、lg[2] =1、lg[3] = 1、lg[4] = 2、lg[5] = 2 ……

设左端点为l,右端点为r,k值的计算公式就是k = lg[r-l+1]。(括号里是长度所以很好记)

区间 1最小值为stmin[l][k]
区间2 最小值为stmin[r-2k+1][k] (长度为2k且终点在r处,起点显而易见:r-2k+1)

阿黄的咒语难度就在stmin和lg这两个数组怎么获得

lg数组

因为是向下取整,我们只要默认后一项和前一项一样,遇到2的指数幂就加1
因为1也是2的指数幂(2^0),所以我们定义lg[0] = -1
-1,0,1,1,2,2,2,2,3,3,3,3……这样一个神奇的数列就出现了

怎么判断i是不是2的指数幂?
这时候就要请来神奇的位运算了,烫知识:只有i是2的指数幂的时候,(i&(i-1)) == 0这个等式才会成立

如图所示,分别展示i为2的指数幂
2,4,6和非2的指数幂3,5,7时的情况

请添加图片描述先初始化lg[0] = -1,后面每一项都和前一项相等,逢2的指数幂加一 即:lg[i] = (i & (i - 1)) == 0 ? lg[i - 1] + 1 : lg[i - 1];

神奇!就像我这么神奇!

stmin数组

stmin[i][0]的值我们是上来就知道的,从i开始,长度为20 = 1区间内的最小值就是它本身。
有了这个stmin[i][0],想求出stmin[i][1]很容易,还记得怎么查询吗?,将想求的区间分成两个,求这两个区间的最小值较小的那个。

求stmin[i][1],肯定分成stmin[i][0]和stmin[i+1][0]了

如图所示,以一个长度为三的序列为例(中间黄色部分)
在上下两个半边分别用stmin[i][0]和stmin[i+1][0](绿色部分)
推出了st[i][1](蓝色部分)

在这里插入图片描述

区间长度为2j,将这个区间一分为二,就是两个长度为2 (j-1)的区间,正好能夹出想要的结果

有了stmin[i][1],我们就能推出stmin[i][2]
有了stmin[i][2]我们就能推出stmin[i]][3]
…………

请添加图片描述
以此类推,所以我们得到递推公式
stmin[i][j] = min(stmin[i][j - 1], stmin[i + 2^(j-1)][j - 1]);

理(借)论(口)存在,实(魔)践(法)开始

下面让我们瞧瞧神奇的阿黄在预处理的时候究竟施了什么魔法

void pre()
{
    lg[0] = -1;
    for (int i = 1; i <= n; i++)
    {
        lg[i] = (i & (i - 1)) == 0 ? lg[i - 1] + 1 : lg[i - 1];//预处理lg数组
        stmin[i][0] = a[i];//预处理stmin[i][0],下一步由stmin[i][0]递推出stmin[i][1]
    }

    for (int j = 1; j <= lg[n]; j++)//j代表长度为2^j的区间,一共只有n个数,2^j<=n,即j<=lg[n]
    {
        for (int i = 1; i + (1 << j) - 1 <= n; i++)//(1<<j) 即 2^j
        {
        //根据已知的stmin[i][j-1]递推出stmin[i][j],所以j的循环要放在外圈
            stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << j-1)][j - 1]);
        }
    }
}

查询操作就更简单了

int getmin(int l, int r)
{
    int k = lg[r - l + 1];
    return min(stmin[l][k], stmin[r - (1 << k) + 1][k]);
}

多亏了st表,阿黄才能打败所有恶魔

”阿黄这么厉害,这一阵一定过的很轻松吧

阿黄望着村口几座大山,叹了口气

“是啊,很轻松…吧”(妈的现在一看两年前的子弹正中眉心)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值