数列分块入门

分块:将原序列处理成若干个小块,目的是尽量地达到处理和询问之间的平衡。

一般将区间内不完整的块单独暴力处理,完整的块则直接打上标记整块处理。

分块入门 1

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,单点查值。

给每块设计一个加法标记,然后不完整的块直接加,完整的块就打上标记,询问时加上标记即可。

Code

分块入门 2

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

我们对每个块记录一个最大值和最小值,然后如果最大值小于 x ,则直接加上块内元素个数,如果最小值大于 x ,则可以直接跳过这个块,而对于不完整的块,也是直接暴力对每个元素进行判断。

Code

分块入门 3

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(这里的前驱必须小于 x )。

这里我们可以用 set 做。每个块的元素都用一个 set 储存,询问时,不完整块暴力判断,完整的块内使用 set 的 lower_bound 函数查找。

Code

( m 不能等于 sqrt(n) ,否则会 T)

分块入门 4

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和。

线段树的基本操作。使用分块的话,只要用一个数组维护每个块的和即可。

Code

分块入门 5

给出一个长为 n 的数列 \(a_1…a_n\),以及 n 个操作,操作涉及区间开方,区间求和。

区间求和比较好操作,问题在于区间开方。因为是向下取整,所以一个数经过多次开方后的取值一定为 0 或 1 ,显然,一个块内所有元素都是这些值后,再次开方是不影响块内元素和的,所以我们可以利用这一点,对每个完整块也进行暴力开方,判断这块是否全为 0 或 1,打上标记,下次就可以直接跳过这个块,而块外的仍然暴力修改。

双倍经验:BZOJ3211 Luogu

Code

分块入门 6

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

由于数据随机生成,这题显然可以用 vector 直接做。

而如果不是随机的,就有可能一个块内的元素过多,大大超过了 \(\sqrt{n}\) ,那么我们就需要重新分块。

先将每一个块的元素用一个 vector 储存,插入就直接在所在的块的 vector 中插入,然后判断块的大小,如果过大,则重构,直接将每个块的元素储存在一个数组中,然后分块即可,注意要将 vector 清除。

Code

分块入门 7

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。

这里的难点在于乘法和加法的先后。

对于完整块,假设先前的标记为 \((a,b)\) 其中 \(a\) 表示乘法标记, \(b\) 表示加法标记。那么进行区间乘法时,标记将变为 \((a\times k,b \times k)\) ,进行区间加法时,标记将变为 \((a,b+k)\)

对于不完整块,我们暴力将其所在的块整块都乘和加上标记,将标记重置,然后再将包含在区间的元素进行当前操作。

Code

分块入门 8

给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c 。

容易发现,经过几次询问后,数列只有几段的数值不同,与区间开方的题目类似。同样,我们可以打一个标记,记录这个块要修改为的元素,对于不完整块,同样重置标记,暴力修改这个区间所在块内每个元素,然后修改询问所覆盖的区间的值,同时进行统计,而完整的块,如果标记等于 c ,直接加上块内元素个数,而如果没有标记,则暴力判断块内每个元素,然后再对块打上标记。

Code

分块入门 9

给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。

关于最小众数,可以看一下陈立杰大佬的「区间众数解题报告」。

对于区间的最小众数,它存在于整块内的最小众数或是块外的最小众数。

我们先将数列离散化处理,然后预处理出任意块到最后一个块的最小众数,然后询问时仍然是块外暴力,而块内的已经预处理好了,直接比较输出即可。

但我的代码 T 了两个点,修改了 m 的大小并各种加速也还是 T 了一个点......

Code

例题

BZOJ2821 作诗 && BZOJ2724 蒲公英

参考

hzwer大佬的博客

posted @ 2020-01-19 17:22  小蒟蒻hlw  阅读(558)  评论(0)    收藏  举报