差分个人见解(一)
- 一维差分
- 什么是差分
- 构造差分数组
- 差分数组的用处
- 实战演练
- 题目
一维差分
什么是差分
前缀和或许你已经了解了,差分其实就是前缀和的逆运算。
假设 a1 到 an 为 b1到 bn 的前缀和。
那么 b1 到 bn,分别就是 a1 到 an 的 差分。
差分的每一项等于 原序列对应项 减去 前一项。
构造差分数组
构造差分数组有两种方式,第一种是利用下列递推公式遍历一遍数组。
其中 数组 a 为 原数组,数组 b 为要构造的差分数组。
这里跟前缀和那里一样,i 不会 取到 0,数组前都会留一个空位,从1开始存储。
第二种构造方式我们一会 会说到。
差分数组的用处
差分数组可以快速 使一个区间内加上一个数。
我们来看下面的例子,还是假设 a为原序列,数组b 为 a 的差分数组。
假设我们的目标是 3 到 5 这个区间内所有数字都加上 c。
有了差分数组后,我们可以这样做。
首先给 b3 加c。
此时我们如果求 数组b 的新前缀和数组,如果覆盖数组a,那么 a3 到 an都会 加上 c。(这个想法很关键)
由于我们的目标是 a3 到 a5 加上 c。
所以需要这样做。
给 b6 减去c。
这个操作之后,当我们求 数组b 的前缀和数组的时候,a6 到 an 的值都会减去 c。
由此我们便得出了一个公式。
如果我们需要在区间 [ l , r ] 上加一个数 c。
那么我们只需要操作差分数组就可以了,公式如下:
实战演练
题目
输入一个长度为 n n n 的整数序列。
接下来输入 m m m 个操作,每个操作包含三个整数 l , r , c l, r, c l,r,c,表示将序列中 [ l , r ] [l, r] [l,r] 之间的每个数加上 c c c。
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数 n n n 和 m m m。
第二行包含 n n n 个整数,表示整数序列。
接下来 m m m 行,每行包含三个整数 l , r , c l,r,c l,r,c,表示一个操作。
输出格式
共一行,包含 n n n 个整数,表示最终序列。
数据范围
1
≤
n
,
m
≤
100000
1 \le n,m \le 100000
1≤n,m≤100000,
1
≤
l
≤
r
≤
n
1 \le l \le r \le n
1≤l≤r≤n,
−
1000
≤
c
≤
1000
-1000 \le c \le 1000
−1000≤c≤1000,
−
1000
≤
整数序列中元素的值
≤
1000
-1000 \le 整数序列中元素的值 \le 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
准备阶段:
其中 数组a 用于存储题目当中的输入数据,数组b 为差分 数组。
输入环节:
接下来需要构造 差分数组。
还记得刚才的那个区间 +c 的公式吗?我们先把这个行为写成一个函数。
第二种构造差分数组的思路是 假设刚开始的差分数组都是 0,然后 利用这个 addToRange
将数据加到 差分数组中。
当然也可以用之前的第一种方法。
在构造完差分数组后,接下来该处理 m 次询问了。
对于每次询问,输入一个区间 和 区间所加的值。
接着套函数 就可以了。
最后 我们只需要 求 数组 b 对应的前缀和数组。
这里我们将前缀和 数组也放到 数组b 里面,也就是覆盖掉原数组。
完整代码如下:
#include <iostream>
using namespace std;
const int N = 1e5+10;
int n, m;
int a[N], b[N];
void addToRange(int l, int r, int c)
{
b[l] += c;
b[r + 1] -= c;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) addToRange(i, i, a[i]);
while (m--)
{
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
addToRange(l, r, c);
}
for (int i = 1; i <= n; i++) b[i] = b[i] + b[i - 1];
for (int i = 1; i <= n; i++) printf("%d ", b[i]);
return 0;
}
这个代码其实有些地方的for循环是 可以合并在一起写的,如果你的思路清晰了,就合并着写。
完