—分块是一个暴力优化算法,可以通过暴力优化来保证时间复杂度 (较好的暴力)
(现在才学也是没谁了qwq)
算法实现
假设有一道题目,要求支持区间修改,区间查询,相信大家都会用线段树或树状数组求解,但是,如果题目要求一段区间内修改后权值为 k k k 的个数呢?
肥肠难办呢~~
让我们回归最初的暴力,暴力枚举区间的范围,暴力加减,然后再暴力枚举求解答案(太暴力了),分块就是在这个基础上优化时间复杂度的。
1.修改
假设我们将这段序列分为很多段,那么如果这个区间完全被包含在需要更改的区间内,只需要给这整一个大块打上一个
t
a
g
tag
tag 标记,表示这一个大块内元素一起被加上(减少)多少。
那不完整的块呢?只需要暴力枚举元素强行将初始值更改即可。
2.查询
题目中要求区间查询和权值为 k k k 的个数。
区间查询很好办,只需要记录一个 s u m sum sum 表示一个整块的初始值的和,那么一个整块的贡献为初始值和 + + + t a g ∗ tag * tag∗ 区间元素个数。同样,对于不完整的块,暴力枚举元素,加入其权值以及 t a g tag tag 值即可。
查询权值为
k
k
k 的个数有点难办。。
我们如果使得每一个块都是有序的,那么我们就可以用二分快速求出答案。
很明显,我们可以对于每一个块都排一边序,强行使它有序,二分出最靠右的
k
k
k 和最靠右的
k
−
1
k-1
k−1 ,就可以求出块内的贡献啦!
同样,不完整的块直接暴力求解,更新答案即可
时间复杂度
很明显,只要分的块数为 n \sqrt n n ,那么时间复杂度是最优的。
修改的时间复杂度:
对于完整的块,最多有
n
\sqrt n
n 块,所以时间复杂度为
O
(
n
n
)
O(n \sqrt n)
O(nn) 的
对于不完整的块,最多有
2
n
2 \sqrt n
2n 个元素(假设左边右边全塞满了),时间复杂度也是
O
(
n
n
)
O(n \sqrt n)
O(nn) 的。
区间查询的时间复杂度:
同修改的一样,也是
O
(
n
n
)
O(n \sqrt n)
O(nn) 的。
查询权值为
k
k
k 的个数的时间复杂度:
排序是
(
n
l
o
g
n
)
(\sqrt n log \sqrt n)
(nlogn) 的(最多有
n
\sqrt n
n 个块嘛)。 ,二分查找也是
O
(
n
l
o
g
n
)
O(\sqrt n log \sqrt n)
O(nlogn) 的。
而暴力枚举部分是
O
(
n
)
O(\sqrt n)
O(n) 的 。
所以总的时间复杂度为
O
(
n
n
l
o
g
n
)
O(n \sqrt n log \sqrt n)
O(nnlogn) 的,比普通的暴力好上不少呢!
总之,分块还是一个十分不错的算法。
当然,分块的进阶版——莫队,还得有分块的前置知识才行~~