第二分块学习笔记
突刺贯穿的第二分块
原题:CF896E - Welcome home, Chtholly
加强卡常版:P4117 - 五彩斑斓的世界
听说原题现在卡常暴力过不了了?不太清楚 QAQ(记得当时第一次过的就是指令集暴力 QAQ)
嗯那么反正看到 Ynoi 就想到卡常 + 分块,然后听说第二分块是最简单的一个??(怪诶
好像这个还有一个「瑟尼欧里斯树」的别名?但是怎么想都和树没关系吧(摔
功能
第二分块可以 在线 解决这样的问题:
- 将区间内所有大于 的数全部减去 ;
- 查询区间内恰好等于 的数有多少个。
对数据的要求是值域不能太大( 差不多了),毕竟是开在值域上的处理。
下面的话用 表示序列长度, 表示询问个数, 表示值域大小。
原题题意(CF896E)
给定一个长度为 的序列和 次上面的操作。数据范围:,时限 ,空间 .
加强版卡了空间()还扩大了值域(),所以必须离线下来做了lxl 属实毒瘤。
当然离线下来可以把空间复杂度减到 .
思路
很显然每个区间的最大值是递减的。不过好像没什么用
先分块,块长度还是传统的 . 然后观察数据范围和操作。应该能想到要在值域上做一些事情。
查询
对于查询而言,我们可以在每个块里都开 个结点(相当于值域上的并查集),然后把序列里每个位置存一个指针指向它的值对应的结点。
那么整块的查询就可以直接查 对应多个结点有几个指针指过来,散块就暴力找。复杂度 .
修改
然后考虑修改怎么做。对于整块的区间减,可以记录这个块的最大值 . 一种想法是把 的所有值的结点分别和 合并:
合并前(假设 ):
before merge合并后:
after merge
但是这样做如果 太小或者 太大,就会合并出问题:合并到被合并的地方(图中 )。
什么时候合并会出错?显然是 .
再次观察修改操作,我们发现将「大于 的数全部减 」和「将小于等于 的数全部加上 ,然后再区间整体减去 」是等价的。因此我们如果对每个块记录一个 tag,区间减时直接修改 tag 就可以了。
因此我们做分类讨论:
- 即 即 (注意 ),此时直接合并会出错,需要反方向合并然后打上 tag;
- 即 即 ,此时直接合并即可。
那么显然这时候的查询就需要考虑一下 tag 了。
那么整块修改的复杂度最坏是 的,其中 是使用数据结构进行合并的复杂度。同时也很显然可以通过并查集或者链表支持合并操作。如果用并查集,可以证明在这里合并的情况都是 的!
至于散块的修改,可以直接暴力把块打散了重构做,复杂度 .
以及并查集不能像平常一样初始化,不然时间会爆…… 然后好像说可以证明直接靠特性初始化为 0 是没有问题的?不会 /kk
代码实现
细节问题一堆。。。咕了(泪