分块学习笔记

前言:

分块——高级的暴力。采用的主体思想是“整块打标记,散块打暴力”。能解决很多线段树,平衡树,树套树等数据结构类问题,复杂度高但常数很小,可以和一些大常数的双log甚至单log数据结构持平,且思维相对简单,缺点是码量较大,需要一定的代码能力。

上篇——静态分块

区间分块

最简单的东西,感觉也是变化最多的东西

把数组分成 n 段,每段的长度是 n

分块开不开结构体都可以,一般需要对每个块维护st,ed,并对每个点维护num,表示该点在哪个块中。

值域分块

分块的进阶版,在区间分块的基础上对值域分块,为了保证修改和查询时正确的复杂度,一般采用前缀和优化。即设sum(i,u)代表第1i块,落在值域上第u块的总个数,再设val(i,u)代表第1i块,数值为u的总个数。

则修改和查询操作都可以分为:

{

值域分块可以解决一些诸如带修改区间第k小等原本需要树套树才能解决的问题,赢!

下篇——块状链表(动态分块)

“动态”不是指支持带修,这个普通分块也能做,但是块状链表可以支持带插入,即在序列的第k项插入一个数值x,原来后面的数集体向后平移一位。

实现很简单,先分块,把原来分好的块用链表串联起来,也就是对每个块维护nxt,指向它的下一个块。遇到插入则暴力重构整个块。

注意在同一个位置插入过多的数会导致这个块长度远大于n,所以需要一个split函数,当块长过长( 2sqrtn)时,就把这个块分裂成两个。

特殊情况下也会需要一个merge函数,但大多数情况下不用,因为如果每次只删一个数,则无论如何块的个数都不会过多。

块链的写法是对每个块开一个结构体,存len,nxt和原数组,很多情况下也要存stmx,具体情况具体分析。会涉及到求下标x在哪个块内,可以从头开始跳,也可以像普通分块一样维护一个num数组。

这里简单说一下个人总结出的三种块链。

单点插入,全局查询

eg.P3369

最好写的块链,这类题不涉及元素的位置,可以直接维护一个有序的序列,通常要对每个块维护mx,注意插入哨兵节点。

单点插入,区间查询

eg.存储器,P4278

比较常见的块链,一般需要维护num数组和每个块的st,查询正常做,插入时后面所有块的st++,插入和分裂时注意维护num数组。

有时候还可以搭配值域分块食用。

区间插入,区间查询

eg.P2042

比较难写的块链,个人觉得更像一种根号版平衡树(

我们稍微魔改一下split函数,让它可以指定从某个位置把一个块分成两个,那么对一个区间的操作就可以转化为对一堆整块的操作,注意最后要merge回去,同时注意块的个数要开多一点。

同时特别说一下区间反转,转化为一堆整块之后,把每个块打个tag,同时它的nxt指针都指向原来自己前面的数,然后两边特殊处理一下即可。注意块内如果维护了前后缀相关内容,则需要swap一下。单独写一个下传标记,重构整个块。

posted @   gin-001  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示