数列分块入门
分块:将原序列处理成若干个小块,目的是尽量地达到处理和询问之间的平衡。
一般将区间内不完整的块单独暴力处理,完整的块则直接打上标记整块处理。
分块入门 1
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,单点查值。
给每块设计一个加法标记,然后不完整的块直接加,完整的块就打上标记,询问时加上标记即可。
分块入门 2
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
我们对每个块记录一个最大值和最小值,然后如果最大值小于 x ,则直接加上块内元素个数,如果最小值大于 x ,则可以直接跳过这个块,而对于不完整的块,也是直接暴力对每个元素进行判断。
分块入门 3
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(这里的前驱必须小于 x )。
这里我们可以用 set 做。每个块的元素都用一个 set 储存,询问时,不完整块暴力判断,完整的块内使用 set 的 lower_bound 函数查找。
( m 不能等于 sqrt(n) ,否则会 T)
分块入门 4
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,区间求和。
线段树的基本操作。使用分块的话,只要用一个数组维护每个块的和即可。
分块入门 5
给出一个长为 n 的数列 \(a_1…a_n\),以及 n 个操作,操作涉及区间开方,区间求和。
区间求和比较好操作,问题在于区间开方。因为是向下取整,所以一个数经过多次开方后的取值一定为 0 或 1 ,显然,一个块内所有元素都是这些值后,再次开方是不影响块内元素和的,所以我们可以利用这一点,对每个完整块也进行暴力开方,判断这块是否全为 0 或 1,打上标记,下次就可以直接跳过这个块,而块外的仍然暴力修改。
分块入门 6
给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。
由于数据随机生成,这题显然可以用 vector 直接做。
而如果不是随机的,就有可能一个块内的元素过多,大大超过了 \(\sqrt{n}\) ,那么我们就需要重新分块。
先将每一个块的元素用一个 vector 储存,插入就直接在所在的块的 vector 中插入,然后判断块的大小,如果过大,则重构,直接将每个块的元素储存在一个数组中,然后分块即可,注意要将 vector 清除。
分块入门 7
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间乘法,区间加法,单点询问。
这里的难点在于乘法和加法的先后。
对于完整块,假设先前的标记为 \((a,b)\) 其中 \(a\) 表示乘法标记, \(b\) 表示加法标记。那么进行区间乘法时,标记将变为 \((a\times k,b \times k)\) ,进行区间加法时,标记将变为 \((a,b+k)\) 。
对于不完整块,我们暴力将其所在的块整块都乘和加上标记,将标记重置,然后再将包含在区间的元素进行当前操作。
分块入门 8
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间询问等于一个数 c 的元素,并将这个区间的所有元素改为 c 。
容易发现,经过几次询问后,数列只有几段的数值不同,与区间开方的题目类似。同样,我们可以打一个标记,记录这个块要修改为的元素,对于不完整块,同样重置标记,暴力修改这个区间所在块内每个元素,然后修改询问所覆盖的区间的值,同时进行统计,而完整的块,如果标记等于 c ,直接加上块内元素个数,而如果没有标记,则暴力判断块内每个元素,然后再对块打上标记。
分块入门 9
给出一个长为 n 的数列,以及 n 个操作,操作涉及询问区间的最小众数。
关于最小众数,可以看一下陈立杰大佬的「区间众数解题报告」。
对于区间的最小众数,它存在于整块内的最小众数或是块外的最小众数。
我们先将数列离散化处理,然后预处理出任意块到最后一个块的最小众数,然后询问时仍然是块外暴力,而块内的已经预处理好了,直接比较输出即可。
但我的代码 T 了两个点,修改了 m 的大小并各种加速也还是 T 了一个点......
例题
BZOJ2821 作诗 && BZOJ2724 蒲公英

浙公网安备 33010602011771号