decoqwq
2018-07-14 12:10:34
已update(感谢codesonic添加qwq)
分块算法实质上是一种是通过分成多块后在每块上打标记以实现快速区间修改,区间查询的一种算法。其均摊时间复杂度为
分块算法相较于各种树形数据结构,具有简便易写,方便调试等多种优点。在同等数据规模下,如
接下来讲一下分块算法的基本操作及性质:
为了使得其有着最稳定的时间复杂度,我们经常讲一个长度为
如下图,就是一种序列的分块:
该长度为
而我们要记录的一个值,就是每个序号代表的数,属于哪一块
如上图,
可以得到获取每一个序号的所在块的代码是:
int n;//总个数
int block=sqrt(n);//每一块大小
for(int i=1;i<=n;i++)
{
belong[i]=(i-1)/block+1;//每一个数所在块
}
但是,如何用分块来维护区间最值?
我们举个例子:
给定一个长的为n数列,求出任意区间[l,r]的最大值(1<=l,r<=n)(l<=r)
可能你会说:我会线段树!
但是拿分块又怎么做呢?
还是拿这张图,我们现在给每个点上加了一个权值,每个块维护一下块内最大值
当我们查询任意一个区间
若其不在一个块但是块是相邻的,一样是暴力查询,时间复杂度
若其块不相邻,如
所以总时间复杂度
那如果加入了区间修改,又该怎么办呢?
对于整块修改,我们打个加法标记,即当前块增加了多少,最大值相应的就增加了多少
而多于边块角块,暴力修改,特判最大值即可
所以总时间复杂度也是
分块还能解决很多很麻烦的问题,比如寻找区间内前驱后继
不好意思树套树太麻烦了
分块解法也非常的好理解,当我们分块的时候,就对每一个块进行排序,查找时,边块角块依旧暴力,整块使用
举个例子 LOJ6279
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
此题查找前驱,我们直接使用上述算法即可
代码见这里
分块其实是一种树形结构,它是一种只有三层的树,形态如下:
第一层为整个序列,第二层为
莫队算法的思路是,离线情况下对所有的询问进行一个
我们用分块的方式,来查询被修改了的值,将重新寻找区间最值的复杂度降到了一个非常低的水平,这就是分块在莫队中的应用。
例题:洛谷P1494,可作为莫队入门题
莫队的时间复杂度均摊为
带修莫队,是建立于莫队基础之上的一种算法。当原序列值受到改变时,莫队中存储的答案必定会改变,那么我们如何避免修改操作带来的影响呢?
首先我们需要把查询操作和修改操作分别记录下来。
在记录查询操作的时候,需要增加一个变量来记录离本次查询最近的修改的位置,你需要一个变量记录现在已经做了几次修改。如果改多了,你就要改回去qwq
所以可以得到带修改的莫对分为三维,左指针,右指针,以及当前修改次数
和普通莫队一样,我们先对所有操作
/*基础莫队操作*/
while(l<Q[i].x) Delet(a[l++]);
while(l>Q[i].x) Add(a[--l]);
while(r<Q[i].y) Add(a[++r]);
while(r>Q[i].y) Delet(a[r--]);
/*基础莫队操作*/
/*继续修改的操作*/
while(now<Q[i].pre) Work(++now,i);
while(now>Q[i].pre) Work(now--,i);
/*修改回去的操作*/
out[Q[i].id]=ans;//统计答案
均摊时间复杂度因为修改的存在,变为了
例题:洛谷P3939,可直接跑带修莫队