分块学习笔记
分块学习笔记
分块是基于区间修改和查询的工具。
用一道题来引入:
给出一个长为n的数列,以及n个操作,操作涉及区间加法,单点查值。
这道题如果用暴力来求解的话,需要 O(n2) 的时间复杂度,这样我们是不满意的,那怎么办呢?我们想到,如果 l=1,r=n 时,我们是否可以直接记一个变量进行记录现在已经加了多少值了,所以思路就出来了。
但是,l=1,r=n 的情况太少了,那能不能分为几组进行加值呢?
当然是可以的,我们直接分为 k 组 (当然,k组的长度尽量是一样的)。
在区间加时,在最左边的组和最右边的组之间用 O(k) 的时间复杂度进行遍历,依次加上对应的值,中间我们直接在一个这个组的 add 数组里加一个值即可,O(n/k)的时间复杂度。
那区间查询怎么做呢,那还不简单,直接原来的值加上这个组的值即可,O(1)的时间复杂度。
总时间复杂度 O(n∗max(n/k,n))。
这里要使时间复杂度尽量的小,使 n/k=n 即可,所以 k=√n 是最佳情况,总时间复杂度 O(n√n)。
所以,分块的核心思想就是l=1,r=n的解决方案加上 l=r的解决方案。
再来一道题:
沿用我们上面的思想,如果 l=r,那我们就直接判断即可,如果 l=1,r=n,我们是不是排序之后二分查找即可,那查询就不难了。
那修改呢?这也不难,直接按照上面那一题的方法即可,在查询的时候之间查询 x−add 即可。
再来看一道较难的题目:
区间开方?这个有点难办呀,但是我们想到,这个区间开方能开很多次吗?是不是每一个块至多开方六次就会全都变为零,这样开方的均摊时间复杂度就是 O(6n) 了,其他的都和上面的一样。
看一道不太一样的题目:
好,这里的单点插入我们好像没有见过,思考一下要用什么实现单点查询。
数组的插入是 O(n) 的,而链表的查询是 O(n),这里使用 vector 是最佳的,因为 vector 自带 insert 函数,更简单吧。
当你需要查询的时候直接遍历过来,而插入的时候也是遍历过来,操作的时间复杂度是 O(√n)。
这样的时间复杂度就是 O(n√n) 的。
但是这样如果他一直把数放在一个块里呢?
那我们可以定期重构一下 vector 就可以了。
练习:
loj数列分块入门 1-9