差分 & 前缀和

我们从问题入手。

问题一:有已赋值的 n 个数,现在有 m 个指令,每一次要求将第 k 个数加上 a,最后求所有数的值。

$1 ≤ k ≤ n,m ≤ 10^5$.

这一题其实用一个数组就可以维护。

问题二:有已赋值的 n 个数,现在有 m 个指令,每一次要求将第 x 个数到第 y 个数加上 a,最后求所有数的值。

$1 ≤ x ≤ y ≤ n,m ≤ 10^5$

其实这题分块、线段树、树状数组以及其它的很多算法都可以过这题。

但是请看下一个问题:

问题三:有已赋值的 n 个数,现在有 m 个指令,每一次要求将第 x 个数到第 y 个数加上 a,最后求所有数的值。

$1 ≤ x ≤ y ≤ n,m ≤ 5*10^6$

我们发现:这个 5e6 的范围太大了,O(n log n) 以及更慢的复杂度是过不了的。

所以,步入我们的正题:差分。


差分

① 什么是差分?

差分,就是一个通过常数复杂度的标记实现求和及询问的一种技巧。

一般适合静态维护(前面先加,最后求值)。

② 差分怎么用?

我们马上举一个例子:

有一个数列:

    1  9  2  8  3  7  4  6  5
tag:  0 0 0 0 0 0 0 0 0  // 现在还没有打上标记

现在,我们将第 3 个数到第 5 个数加上 2.

那我们就发现,可以把第 3 个数开始把后面所有的数加上 2,再从第 6 个数开始把后面所有数减去 2 呢,就相当于把第 3 个数到第 5 个数加上 2 了。

然后这么标记:

a[i]:  1  9  2  8  3  7  4  6  5
tag :  0  0  2  0  0  -2 0  0  0  // 打上标记*1

然后,我们再将第 4 个数到第 8 个数减去 1.

按照上面的想法,我们可以从第 4 个数开始把后面所有的数减去 1,在从第 9 个数开始把后面所有的数加上 1.

a[i]:  1  9  2  8  3  7  4  6  5
tag :  0  0  2 -1  0 -2  0  0  1  // 打上标记*2

假设指令执行结束,那么我们这样求和:

用一个 sum 数组记录,$sum_i$ 代表前 i 个数所带的 tag 的和。

a[i]    :  1  9  2  8  3  7  4  6  5
tag     :  0  0  2 -1  0 -2  0  0  1  
sum[i] : 0 0 2 1 1 -1 -1 -1 0

最后,我们把 $a_i$ 加上对应的 $sum_i$ 相加,就是答案了。

时间复杂度 O(n).

猜你喜欢

转载自www.cnblogs.com/zengpeichen/p/11279207.html