「数列分块入门」LOJ6277 To LOJ6285

LOJ6277 To LOJ6285 - Click Here

9/9

  

前言

  感谢KsCla的关心。

  

LOJ6277

Solutions

  考虑对序列分块。对于修改操作,若包含整个块则用个tag保存,否则暴力修改。

  设每块元素为$m$,则时间复杂度$O(n(\frac{n}{m}+m))$。当$m$取$\sqrt n$时,时间复杂度$O(n\sqrt n)$

  

CODE

CODE - Click Here

  

LOJ6278

Solutions

  考虑对序列分块,对于每个块都维护其升序序列。

  对于修改操作,若包含整个块,则用tag保存,否则暴力修改,并重新维护;对于查询操作,需要对整块二分查找。

  设每块元素为$m$,则时间复杂度$O(n( \frac{n}{m} \, \log \,m+m \, \log \, m))$。当$m$取$\sqrt n$时,时间复杂度$O(n \sqrt n \, \log \, \sqrt n)$

  

CODE

CODE - Click Here

  

LOJ6279

Solutions

  做法与LOJ6278一致。

  

CODE

CODE - Click Here

  

LOJ6280

Solutions

  LOJ6277的加强版,从单点查询变为区间查询。

  对于区间查询,可以考虑对每个块维护块内元素和,这样区间查询时即可快速得到整块的答案。

  时间复杂度分析同LOJ6277,$O(n \sqrt n)$

  

CODE

CODE - Click Here

  

LOJ6281

Solutions

  对于数字$a$,其开根号次数大概在$log \, log \, a$左右。因此,对于int范围的整数,至多6次数字就变为0或1。

  基于这个事实,我们可以对于根号操作暴力修改,如果某个块均为0或1的话,则可以跳过修改。而查询操作与上文类似。

  现在考虑下时间复杂度。在每次修改中,整块至多修改6次,即$6n$,而对于不完整的块内的元素,本身受到块大小限制,因此不会有问题。这样,这题就能够解决了。

  时间复杂度分析类似LOJ6280,$O(n \sqrt n)$

  

CODE

CODE - Click Here

  

LOJ6282

Solutions

  若暴力插入,时间复杂度为$O(N)$。考虑对序列分块,这样插入时间复杂度与块大小相关。再加之数据随机可知块大小是期望增加$\frac{Qm}{n}$的(其中m为块大小),块大小只是常数上的变化,因此可令$m=\sqrt n$得到$O(n \sqrt n)$的做法。

  现在来考虑下数据不随机的做法。由于数据不随机,可能会存在疯狂往一个块里插入的情况,这样时间复杂度会倒退到暴力复杂度。因此,需要维护每个块来保证其时间复杂度不会倒退。

  这里引入一个重构的思想。当某个块大小过大或者操作数过多时,选择重新对新序列分块。下面给出的是限制操作数做法,阀值为$\sqrt m$。此种做法在随机情况下劣于限制块大小,因此常数略大。

  

CODE

CODE - Click Here

  

LOJ6283

Solutions

  其实和LOJ6277一样,只是多了一个乘法。注意好乘法优先度大于加法,然后分别维护tag即可。

  时间复杂度分析同LOJ6277,$O(n \sqrt n)$

  

CODE

CODE - Click Here

  

LOJ6284

Solutions

  一道相当有趣的题。

  注意到序列不同颜色数是与操作数相关的。基于这点,考虑对序列分块,并对每个块维护一个tag,表示该块是否颜色相同。对于查询操作,如果整块颜色不同则暴力查询,否则$O(1)$得到答案。

  现在分析一下时间复杂度。原序列可以认为是由一个纯色序列$n$次修改得到的,因此假定序列一开始颜色相同。对于一次操作,若要达到$O(N)$的复杂度,必须保证其区间内不存在一个纯色的块,即需要$\frac{N}{m}$(其中$m$为块大小)次操作才能够使得该操作达到$O(N)$复杂度,均摊时间复杂度为$O(m)$。同时,单次操作的时间复杂度为$O(m+\frac{N}{m})$,其数值大于均摊时间复杂度,所以可以将每次操作时间复杂度认为是$O(m+\frac{N}{m})$。令$m=\sqrt N$,则可知该做法时间复杂度$O(N \sqrt N)$。

  

  这题其实还有$\log$的级别做法。其思路的来源大概是因为,分块本质上是一个$\sqrt N$叉树,根据这点类推到二叉树上。但是对于任意的$m$叉树是否都能有类似复杂度,我并没有推出来……

  其构造方法与分块类似,对于每个节点都记录一个tag,用于保存其子节点是否为相同颜色,然后询问就暴力在二叉树上寻找即可。

  同样的,来分析下复杂度。我们认为原序列一开始是纯色序列。对于一个操作,其操作区域对应二叉树上的$\log N$个节点。而经过一次操作后,只有包含最左或最右节点的节点会更改其tag。换言之,一次操作会导致$\log N$个非纯色节点的产生。而一次查询操作,需要的时间复杂度为$\log N$加上其包含的所有非纯色节点,并且在查询过后,这些节点都会变为纯色节点

  现在定义非纯色节点数为势函数,则势函数始终大于等于0。又因为均摊代价里,势能的变化量和查询的额外代价相抵消,因此得到最终时间复杂度是$O(N \log N)$的。

  

CODE

CODE(分块做法) - Click Here

CODE($N \log N$做法) - Click Here

  

LOJ6285

Solutions

  经典的区间众数问题。

  首先是一个比较显然的做法,莫队算法。考虑到对于一个区间,如果维护以数量为第一关键字,数值大小为第二关键字的堆,便能在$\log N$的时间内转移左右端点。因此直接跑莫队算法即可。

  

  接着是分块做法。对于这题,有一个重要的结论,假设$mode(A)$表示$A$集合的众数集合,那么$mode(A \cup B) \subseteq mode(A) \cup B$。这个结论比较显然,思考一下即可得到。基于这一点,可以得知,如果求出任意区间的众数的话,需要得到完整块的众数,以及任意元素在区间内的个数

  先来解决任意元素在区间内的个数。这个很好解决,可以对于每个元素,都记录其在哪些位置出现过。然后询问区间内个数,等价于询问位置集合对应的前驱、后驱,直接二分即可。那么对于完整块的众数,也可以用同样的方法解决,看成是一个完整块与非完整块的合并。根据简单的分析可知,该做法时间复杂度为$O(N \sqrt N \log N)$。

  

  当然,这种做法显然并不优秀。我们考虑优化这种做法。上诉做法的瓶颈仅仅在于如何求出任意元素在区间内的个数。由于非完整块的元素是需要扫描一遍的,因此其实只需要知道任意元素在块内的个数。这个只需要对每个元素求其块的前缀和即可。根据简单分析,可知时间复杂度为$O(N \sqrt N)$。

  

  最后跑出来,只有$N \sqrt N$跑过去了,其余两个都TLE的……

  

CODE

CODE(莫队算法) - Click Here

CODE($N \sqrt N$做法) - Click Here

CODE($N \sqrt N \log N$做法) - Click Here

本站总访问量次 | 本站访客数人次

Powered by Hexo | Designed by iTimeTraveler | Refined by CSHwang