分块
写一点。
数列分块入门6,主要是定期重构,如果数列的形态改变的话,那么设定阈值为每至少 \(\sqrt n\) 次操作做一次重构,时间复杂度是直接根号的。
数列分块入门8,主要是势能分析(好像是),统计一个区间的最大值和最小值,这个是容易统计的,然后你考虑一个区间询问有多少个相同的,对于最大值和最小值有疑问的就暴力扫一下否则就不管了,然后区间赋值,中间整块势能没了,旁边的加1
数列分块入门9,考虑离散化之后我们可以预处理出从第 \(i\) 个整块到第 \(j\) 个整块之间的众数,而区间询问的误差在于旁边的散块,于是我们考虑对于每个颜色我们先把散块的拉满然后再把整块区间的拿下,然后和预处理的整块答案比较一下取优的就行。
P4119未来日记,考虑先看询问再看修改,询问问区间k小,其实这个东西是很经典的问题了,但是这个修改就搞得挺难受的,考虑对于每个整块也做一个值域分块,然后每次相当于是在值域上面做 \(\sqrt n\) 次单点修改,这样维护值域之后能做什么?
我们考虑求第 \(k\) 大一直有一个很传统的方法就是直接二分答案后问排名,我们把这个套用过来,一段区间内的这个排名,我们考虑散块直接暴力问,整块的话我们统计一个值域分块上的前缀表示在这个区间内这个权值的数的数量(因为这个可减),但是我们做了二分之后不能直接求吧,对这个值域我们还得求个前缀和,如果要在这个修改的基础上维护前缀和的前缀和单log很难做到,但是可以直接值域分块,然后求一个的时间复杂度降为根号,而修改操作因为要修改后面的前缀和,这个随便拿个数据结构来维护就好了。
但是这个散块没法维护啊,实际上作为一个单点直接向上面那样维护就好了,时间复杂度根号带log。
upd:果然水平还是太低,根号log算法过不了啊!
考虑kth其实有一个根号做法就是对值域分块后枚举整块然后枚举散块可以做到单次根号(这个是真没想到,看题解了才发现,知道这个后面的就全都明白了),但是我们这个没办法这么做,我们有整整根号次修改,对块内的倒是影响不大,块外的就闹翻了,不过其实问题也不大,因为每次都只会动两个权值的前缀和,那么对这两个权值所在的块重做前缀和时间复杂度只有根号,这样两边时间复杂度都降下来了。
upd:果然水平还是太低,没考虑标记问题
刚开始的思路是并查集维护代表权值然后似了,考虑这个东西只要一合并就拆不开了,或者说难得拆开,所以每次如果没出冲突就不管出了就重构的时间复杂度是正确的。
然后就是卡常,卡了一上午加半个下午才过。。
本来想开末日三问,写了一大溜不会就贺题去了,发现不会闵可夫斯基和,就暂且搁置了!
然后开了P8937,不知道叫啥,看这个头图管他叫短发女生吧(
完全不会分散层叠,所以进行一个学习。
学完了,再进行一个第二分块的学习,第二分块,感觉不是很难,本质就是考虑整体分三块,小的管不着,大一点的掉一半,大的多了不变,然后中间根号分治分析下复杂度就没了。
铃音的第二分块
考虑第一篇题解所说,我们以 \(b\) 为底数分块(我觉得我这辈子都想不到,记一下trick),然后总共是 \(O(1)\) 块,考虑对于 \(x\) 所在的需要仔细分析的块里面,容易证明一个要被删减的数,顶多被这样操作 \(b\) 次之后就会进入下个块,而我们知道块总数是 \(O(1)\) 的,而比他小的不管,比他大的可以直接打偏移量标记,而比他大的如果出现了转块就暴力,是 \(O(1)\) 的。
然后问题就彻底转化成了O(n)次区间rnk,O(nb)次单点修改和O(n)次区间修改
那我们先上序列分块,先考虑整块那么区间修改其实就变成了O(n\sqrt n)次区间修改,但是不管怎么样本质是不变的,我们只需要上标记然后把小的直接拿下就行了,这一部分是块数的
然后就是整块的单点修改,修改总共有 O(nb) 次,这个是不可能改变的,那么我们首先二分找到这个位置然后直接做,这一部分是块数乘log块长的
然后就是区间rnk,这个东西小的可以直接拿,大的就是块数乘log块长的。
然后是散块,我们整块造了那么多孽,现在怎么复原散块啊,首先是位置,我们每次做暴力单点修改都会让一个权值变位置,容易发现整块做这些都是不变的,我们需要知道的是一个点对应的权值咋变了,按权值意义上我们是维护了的,但是按下标意义我们就完全不晓得他到哪去了,而且我们之前说的操作都建立在【我们能快速找到维护目标】的基础上(虽然通过二分确实可以全部找到)
考虑总数是确定的,在总数上二分的时间复杂度应该要是正确的才对吧。
对于一个权值不一定要是一个权值,考虑用set存储是比较妥当的,因为对于一个点顶多变这么多次。
然后每次查询一个点都作为一个点存在,自然是可以用一个之外的编号维护这些,然后就做完了,时间复杂度是根号log的,调块长不知道能不能把log套进去,因为容易发现查询的log只与块数有关,而修改的log也是。
该说很多可以枚举的地方都直接二分了,实际上哪来这么麻烦,做修改的时候实际上一次二分都不应该有的,而做的插入删除都建立在nb或者n的基础上,完全没问题,所以修改的时间复杂度其实是 O(n\sqrt n)的
而查询是必须用一个log了,但是这个log是前面的是乘的块数,只要对块数做文章就行了。
非常无聊所以没有写。。
P5397第四分块
该说完全没思路也太离谱了,这几天确实摆,看完题解之后觉得自己是纯种傻逼才想不出这种唐诗题。
考虑序列分块,第二分块的形式可以完美解决修改,考虑询问。
对于每个整块,考虑对每两种权值进做他们之间的距离差(这样预处理的时候一个块需要块长的平方的时间复杂度),并且记录最左和最右,然后很明显是可以直接转移的,但是修改就遇到问题了,如果有一个赋值操作,咋办,考虑假设第 \(i\) 种颜色被赋为第 \(j\) 种颜色,那么第 \(j\) 种颜色的状态是会变的,这个变其实就是合并第i种状态和第j种状态,可以直接维护。
而时间复杂度呢,考虑有根号个块,算他每次都要这样做合并,一个块最多被合并根号次,合并一次需要做根号时间的询问,总共三个根号,仍能过。
500ms也太夸张了这个。
P5399第七分块
我有预感这玩意要折磨我一个星期
先考虑进行一下树分块,但是感觉大伙定义树分块都不太一样,所以还是小写一下。
首先树分块的本质是分一大堆簇,簇是啥可以去翻我 TopTree 的博客,暴力分簇让簇大小维持在根号级别就是树分块的原理。
我们称【清算 \(x\) 的子树】为在 \(x\) 这个点开始将其子树分块,分得越少越好,在每一块中,x 为一个端点,而另一个端点则是子树内自己定的,当下面没清算过的时候就是随便掰的叶子,下面清算过的时候就是被清算的点。做完这些之后把 \(x\) 的真后代全部拿下,并把 \(x\) 标记为被清算过的结点。
但是一看就知道这样分块肯定不对!考虑如果一个块下面有若干个端点2未免也太尴尬了吧!
所以当一个块下面端点2的数量超过2的时候就做一次清算,当子树点数超过块长的时候也做一次清算,这样分块的时间复杂度呢?
考虑一个点之所以是端点,是因为他有块长 \(B\) 个点在子树下面,而合并需要两个条件,要么【消耗】了 \(B\) 个点,要么【合并】了两个代表了 \(B\) 个点的点,容易发现第一个操作得来的点的数量是 \(\frac{n}{B}\),而第二个操作得来的点的数量是 \(\frac{n}{B}\) 的,所以如果能做得完全应该是 \(2\frac{n}{B}\) 的块数,但是实际上最大能分出 \(6\frac{n}{B}\) 的块数,我也不知道怎么来的。