分块【2023.2.1】

文章目录

  • 一.引入
  • 二.算法介绍

一.引入

没有引入就是最好的引入

——鲁迅 \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad ——鲁迅 ——鲁迅

为了骗分, O I e r OIer OIer们研发出了 O ( n n ) \mathcal{O(n \sqrt n)} O(nn )的算法——分块1(我不会说是我想不出来引入

二.算法介绍

分块 —— —— ——优雅的暴力

—— c q b z l h y \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad \qquad ——cqbzlhy ——cqbzlhy

在2009年的你面前有一道灰题2

有一段连续的序列 a 1 a_1 a1 ~ a n a_n an,然后现在我们需要执行几类操作:

(一)1 l r:求出 [ l ,    r ] [l, \;r] [l,r] 区间的和

你心里大喜,一看就是前缀和,记录 ∑ k = 1 i a k \sum_{k=1}^{i}{a_k} k=1iak s u m i sum_i sumi,然后显然有 s u m i + 1 = s u m i + a i + 1 sum_{i+1}=sum_i+a_{i+1} sumi+1=sumi+ai+1,我们要求 ∑ i = l r a i \sum_{i=l}^{r}{a_i} i=lrai就直接输出 s u m r − s u m l − 1 sum_r - sum_{l-1} sumrsuml1呗,就一道黄题怎么会是灰题呢?

(二)2 l r x:将 [ l ,    r ] [l, \; r] [l,r]区间加上 x x x

你微微一愣,但是你会一种叫线段树3的数据结构 ! ! 1 !!1 !!1, 完全可以水掉这道题.

(三)3 l r x:在 [ l ,    r ] [l, \; r] [l,r]这个区间中,查询小于 x x x的前驱(比其小的最大元素)。

你暗暗吃惊,然后发现平衡树4可以水掉这道题.

数据范围:空间限制: 4 M B 4MB 4MB, 时间限制:1500 ms, 对于 100 % 100\% 100%的数据, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105(我们万恶的竞赛教练就是这么出的

你…倒在了电脑前,被一道小小的灰题打败了。

你不知道的是,一年后的莫涛队长(被尊称为莫队)发明了两种算法(莫队和分块,完美的解决了此类问题)

分块,顾名思义,就是把一段序列分成一小块一小块来处理,维护。

我们把一段当成一个整体,只记录维护整体的有关信息,就是分块。

首先,对于前言说得那道题,很朴素的做法就是:

1.从询问区间的l到r扫过去,每回加上扫到的值,即 a n s = ∑ i = l r a i ans=\sum_{i=l}^{r}{a_i} ans=i=lrai

2.直接把 a i a_i ai重新赋值不就得了 a i + = x ( l ≤ i ≤ r ) a_i += x(l \leq i \leq r) ai+=x(lir);

3.从询问区间的 l l l r r r扫过去,每回遇到 < x <x的位置,答案记录最大值

代码:

while(q --){
	int opt, l, r, x;
	read(opt), read(l), read(r);
	if(opt == 1){
		int sum = 0;
		for(int i = l; i <= r; i ++)sum += a[i];
		write(sum);
		putchar('\n');
	}
	else if(opt == 2)for(int i = l; i <= r; i ++)a[i] += x;
	else{
		int res = 0;
		for(int i = l; i <= r; i ++)
			if(a[i] < k)res = max(a[i], res);
		write(res);
		putchar('\n');
	}
}

没错,这种做法很傻是不是?(我可能是真的傻子,这代码都写

但是,分块就是在这个基础上暴力优化的!!!

假设我们总共的序列长度为 n n n,然后我们把它切成 n \sqrt n n 块,然后把每一块里的东西当成一个整体来看,

现在解释几个本文用到的术语:

然后我们先看看怎么得出答案:

1.对于完整的块5,我们希望有个东西能直接找出这整个块的和,于是每个块要维护这个块的所有元素的和。

2对于不完整块6,因为元素比较少(最多有 n n n / 块数 = n \sqrt n n 个) 这时候当 n = 1000000 n=1000000 n=1000000的时候最多有 1000 1000 1000个,对比一下,我们可以直接暴力扫这个小块统计答案,

小技巧:如果这个不完整块被覆盖的长度>块维护的长度的一半,何不用这个块的和-没有被覆盖的元素的值呢?

2.这里,我们换种思路,记录一个lazy 标记(为什么用lazy,因为我很懒),表示整个块被加上过多少了,

.对于完整块,我们直接lazy+=加上的数x,块内的和ans+=x*元素个数(因为每个元素都被加上了x)

.对于不完整块,直接暴力修改就好了,顺便可以把lazy标记清了。

3.哎呀,这个有点难度啊,

.要在每个完整块内寻找小于一个值的元素数,

显然我们不得不要求块内元素是有序的,这样就能用二分(快速在一个有序的序列里查询的一个算法),对块内查询。

.不完整的块暴力就好

.这样的话需要提前对每块里面的元素做一遍排序就好.

.但是当有修改的话,因为整个块同时加上(减去)一个数,每个数的相对大小是不会变的,但是如果是不完全块就会改变,这样的话,还是因为元素个数小,重新新排一下不就得了?

然后,这道题就用了一种看似高大上的方法做完了……比之前傻傻的暴力是不是好看很多呢


  1. 其实这里的时间复杂度并不准确,只是大多数的代码近似于这个时间复杂度 ↩︎

  2. 在洛谷(一个 O I OI OI网站中)题目的难度分为灰, 红, 橙, 黄, 绿, 蓝, 紫, 黑, 灰。难度依次递增,而灰题是未判定难度的题。 ↩︎

  3. 一种树形结构,可以维护区间求和和单点修改的优秀数据结构 ↩︎

  4. 一种更加优秀的数据结构,可以完成大量的操作 ↩︎

  5. 完整块:被操作区间完全覆盖的块 ↩︎

  6. 不完整块:操作区间不完全覆盖的块 ↩︎

你可能感兴趣的:(分块,算法,数据结构,python)