【数列分块入门总结】

分块算法 :优雅的暴力。将一个序列每B个分为一个块,那么对其进行操作的时候,一般都只考虑整块的处理,两端多余部分的处理,常常可以将时间复杂度压到根号。
(之前也做到过一些分块解的题目,但是没有系统的学习过,这次学习一下经典的题目)
之后的 数列大小的操作的次数都在1e5的级数

分块入门一

问题 : 长度为n的数列,以及m个操作,操作涉及区间加减,和单点查值。
分析: 有很多数据结构都可以解决,线段树,树状数组 都可以,而且时间复杂度还低一些。
好了,分块也很好理解。假设我们每B个元素为一个块。
对于区间加法来说,我们可以用一个数组tag来维护当前块加了多少值。每次询问的区间,最坏 涉及到 n/B 个块,还有两端的多余的2*B个元素,对于整块的部分,我们直接加在tag数组上o(1),对于两端的值我么可以暴力修改值 o(B)。
所以总的时间复杂度为 o( m*( n/B + 2*B ) ) 根据均值不等式,我们可以知道当 B=sqrt(n)应该是最小的。

分块入门二 :

问题:一个长为n的序列,以及m个操作,操作涉及区间加法,询问区间内小于某个值x的元素个数。

分析:其实这个题目是 求区间第k大的子问题,对于区间第k大的问题,分块可以求,主席树也可以解决这个问题。

1 我们首先不考虑区间加法这个更新操作,我们先只考虑询问,对于每次询问,我们还是要考虑 对于整块部分和两端的多余部分的处理。两端多余部分,很好弄,逐一枚举就好了。但是整块的部分,我们要提前预处理一下,想一下,如果我们有每一块的有序序列,那么想要求小于x的个数,那么简单,直接二分求就好了,这里也是这个想法,我们先要提前有序化每一块中的序列。考虑一下时间复杂度: 两端的部分枚举o(B) ,中间整块的部分o( n/B * log(B) ),总时间复杂度为 o( B+ n/B * log (B) ) 还是可以在我们接受的范围内 。当B=sqrt(n) 时候,总时间复杂度为o( m*sqrt(n) * log(sqrt(n) ) ) ,实际测试B=2*sqrt(n)好一些 。

现在我们再来讨论一下 对于如果有区间加法会怎么样,还是一样的思路考虑整块部分和两端部分,整块部分,我们可以参考第一个题目,加一个tag数组维护, 那么询问的时候可以二分 x-tag[i] 来求。 两端多余部分,我们会要暴力修改,但是你会发现一个问题,暴力修改的时候肯定会影响这个块的 有序性,所以每次暴力修改之后,我们还要排序一下,来保持当前块的有序性。 和询问操作一样都有一个logB的时间复杂度,但是很明显没有 询问时间复杂度高,所以我们可以认为询问部分的时间复杂度时间复杂度就是总的时间复杂度。

如果还是不太明白,可以做一下例题: 求区间第k大问题(分块题解),如果那个理解了,这个问题只是那个的子问题,肯定没有问题。

分块入门三

问题:给一个长度为n的序列,以及m个操作,操作涉及区间加法,询问区间内小于某个值x的前驱(比其小d的最大元素)。
分析:仔细想一下,是不是和上一个问题很相似,貌似都是可以用二分求出。事实也是如此。
其实本题有另一个启发意义:可以在块内维护其他结构,使其更具有扩展性,比如放一个set,这样如果还有插入和删除元素的操作,会更加的方便,减少代码量。
时间复杂度和上一个题目一样(因为都是同一个操作,不过从lower_bound成了upper_bound)

分块入门四

问题:给长度为n的序列, 以及m个操作,操作涉及区间加法,区间求和。

分析: 有了上述题目的经验,肯定会有一些灵感,区间加法,我们肯定一样用tag数组来维护,而区间求和,我们可以用数组sum维护每一块的和。
更新操作:整块部分,修改tag,两端的暴力修改值,然后更新 所在块的总和sum.
询问操作:整块的用sum+tag*块长 ,两端的直接暴力求每个(值+tag )

分块入门五

问题:长度为n的序列,以及m次操作,操作i涉及区间开方和区间求和。

分析: 乍一看,这个题目感觉很棘手。同样的问题,主要关注,整块部分和两端部分在更新和询问时候怎么样求解。
更新: 两端部分,我们可以逐一枚举,很好做。整块部分是最令人头疼的,整块求和与都开方后求和好像很难转化过去。 这里用了另一个很巧妙的想法,我们会发现 同一个数字 不断的开方,之后必定为两种结果,要么是1,要么是0,而且 开方的次数还不用很多就会达到最后(数 y n次开方为 y ^(n/2) ,所以收敛很快)。所以我们只要维护好每个块是否都是0或1就好了,因为只要都是0或1,那么无论怎么样开方肯定这个快都不用在修改了
询问: 同之前的题目。
线段树也是可以 暴力修改 然后求解的。
这个题目 很新颖的地方就是 ,我们要根据操作(开方)的特点,因为它是向下型的,越处理越小最后为定值的这种,同时收敛的还很快,这样的话,我们可以直接暴力修改,然后求解。

分块入门六

问题: 给出一个长为n的序列,以及m个操作,操作涉及单点插入,单点查询,数据随机生成。

1 数据是随机生成的情况
这里就用到了之前的有一道题目,每一块我们可以用一个别的容器来存储。链表啊,vector ,set什么的。
更新:我们可以直接在每个块中暴力插入,即使是vector容器,因为数据是随机的,时间方面也是够的。另一方面为啦查找好找,我们还可以标记每个块的最后一个元素是整个序列的第几个元素。所以对于每次暴力插入,我们还需要对后面的 块的标记修改一下。
询问:因为有了标记,所以查找就很方便,而且还是vector可以直接访问元素。

2 如果数据不是随机生成的
如果还是像上面一样 ,暴力插入的话,可能会导致一个块中的元素远远大于B(我们一开始规定的块的大小),时间方面无法保证。所以我们可以用重构,每 sqrt(m)次操作,我们重新分配一下块,重新分配的时间复杂度为o(n)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值