差分定义
首先定义差分:前缀和的逆运算
给定已知的数组
a
i
a_i
ai,求数组
b
i
b_i
bi。使得以下条件成立
b
i
=
a
i
−
a
i
−
1
(
i
=
1
,
2
…
,
n
)
b_i=a_i-a_{i-1} (i=1,2\dots,n)
bi=ai−ai−1(i=1,2…,n)
同时这个条件等价于
a
i
=
b
1
+
b
2
+
⋯
+
b
i
a_i=b_1+b_2+\dots+b_i
ai=b1+b2+⋯+bi
此时我们称
数组
b
i
为数组
a
i
数组b_i为数组a_i
数组bi为数组ai的差分,
a
i
为
b
i
a_i为b_i
ai为bi的前缀和
差分的应用
假设我们有某种操作需要进行,这种操作给出一个连续区间 [ L , R ] [L,R] [L,R],接着我们需要对 a L 至 a R a_L至a_R aL至aR的元素分别加上某个值c,如果不使用差分算法,那么这个操作的时间复杂度就是 O ( N ) O(N) O(N).如果使用差分算法,那么这些操作的时间夫再度将被简化到 O ( 1 ) O(1) O(1)。
算法
接下来我们探讨如何利用差分简化上述操作。
如果要令
a
L
a
R
a_L~a_R
aL aR的元素都加上c,那么我们只需让
b
L
b_L
bL加上c,
b
R
+
1
b_{R+1}
bR+1减去c即可,证明如下:
a
L
=
b
1
+
b
2
+
⋯
+
b
L
a_L=b_1+b_2+\dots+b_L
aL=b1+b2+⋯+bL
B
L
=
b
L
+
c
B_L=b_L+c
BL=bL+c
A
L
=
b
1
+
b
2
+
⋯
+
B
L
A_L=b_1+b_2+\dots+B_L
AL=b1+b2+⋯+BL
A
L
=
a
L
+
c
A_L=a_L+c
AL=aL+c
即当
b
L
b_L
bL加上了c,对于任意的
i
(
i
>
L
)
i(i>L)
i(i>L),
a
i
a_i
ai均加上了c,但由于我们指定了区间
[
L
,
R
]
[L,R]
[L,R],所以需要
b
R
+
1
b_{R+1}
bR+1减去c,使得
a
j
(
j
>
R
)
a_j(j>R)
aj(j>R)的值恢复原状。
现在我们若想更新 a L 至 a R a_L ~至a_R aL 至aR的值,只需要修改 b L 与 b R + 1 b_L与b_{R+1} bL与bR+1的值,时间复杂度变成了 O ( 1 ) O(1) O(1)
代码
输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。
输出格式
共一行,包含 n 个整数,表示最终序列。
数据范围
1≤n,m≤100000,
1≤l≤r≤n,
−1000≤c≤1000,
−1000≤整数序列中元素的值≤1000
输入样例:
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1
输出样例:
3 4 5 3 4 2
//差分 时间复杂度 o(m)
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
b[i] = a[i] - a[i - 1]; //构建差分数组
}
int l, r, c;
while (m--)
{
scanf("%d%d%d", &l, &r, &c);
b[l] += c; //将序列中[l, r]之间的每个数都加上c
b[r + 1] -= c;
}
for (int i = 1; i <= n; i++)
{
a[i] = b[i] + a[i - 1]; //前缀和运算
printf("%d ", a[i]);
}
return 0;
}