差分数组:
首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];
然后我们构造一个数组b : b[1], b[2], b[3],,,,,, b[i];
使得 a[i] = b[1] + b[2] + b[3] + ,,,,,, + b[i]
也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。
1、如何拥有差分数组呢(初始化)
其实不要考虑 差分数组 的构造,换个角度考虑从0开始用bn 生成an,这样an生成之后,b数组出来了,具体思路如下:
可以先想象a 全部都是0,那么b 也全部都是
然后怎么样变成{a1, a2, a3 … , an}呢
就是相当于 每个地方,进行了加上这个数的操作
就[1,1] 的区间+a1, [2, 2]的区间+a2 …[n, n]的区间+an
2、一维差分
例如给定一个区间 [l, r] 让a数组,这里边所有的数都加上c,如果是用遍历,那就需要O(n)的复杂度, 如果用差分来做,就可以做到O(1)原理如下:
给a数组中的[ l, r] 区间中的每一个数都加上c,只需对差分数组b做 b[l] + = c, b[r+1] - = c 。时间复杂度为O(1), 大大提高了效率。
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N],b[N];
int n,m;
int insert(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++) insert(i,i,a[i]); //构建差分数组
while(m--)
{ int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l,r,c) //表示将序列中[l, r]之间的每个数加上c
}
for(int i = 1;i <= n; i++)
{
b[i] += b[i - 1]; //求前缀和运算
printf("%d ",b[i]);
}
return 0;
}
3、二维差分
假定我们已经构造好了b数组,类比一维差分,我们执行以下操作
来使被选中的子矩阵中的每个元素的值加上c
b[x1][y1] + = c ;
b[x1,][y2+1] - = c;
b[x2+1][y1] - = c;
b[x2+1][y2+1] + = c;
![](https://img-blog.csdnimg.cn/img_convert/ca9d62ff430242cbc34ba6eaffad2ead.png)
b[x1][y1] += c ; 对应图1 ,让整个a数组中蓝色矩形面积的元素都加上了c。
b[x1,][y2 + 1] -= c ; 对应图2 ,让整个a数组中绿色矩形面积的元素再减去c,使其内元素不发生改变。
b[x2 + 1][y1] -= c ; 对应图3 ,让整个a数组中紫色矩形面积的元素再减去c,使其内元素不发生改变。
b[x2 + 1][y2 + 1] += c; 对应图4,让整个a数组中红色矩形面积的元素再加上c,红色内的相当于被减了两次,再加上一次c,才能使其恢复。
当然关于二维差分操作也有直接的构造方法,公式如下:
b[i][j] = a[i][j] − a[i − 1][j] − a[i][j − 1] + a[i −1 ][j − 1]
#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++)
{
b[i] += b[i - 1]; //求前缀和运算
printf("%d ",b[i]);
}
return 0;
}