学习差分前先需要回顾下前缀和的概念及其定义: 点这里看前缀和概念
我们已知前缀和的代码的实现:s[ i ] = s[ i - 1] + a[ i ];
(式子1)
即数组S存的是数组a的前 i 项和;这个相信很好理解,但是说到差分的定义则容易混淆;
为什么说差分是前缀和的逆运算呢?
我们看下差分定义:差分即相邻两个数的差。
我们直接按照其定义字面意思能写下:s[ i ] = a[i] - a[i-1];
(式子2)
即用数组S记录下数组a的每个相邻的数之间的差。
我们观察这两个式子,将前缀和中的数组s和数组a反以下即有了式子2。
即在数学中,令 s = a, a = s,则 a[i] = a[i-1] + s[i] ,将a[i-1]放另一边变得出
s[1] = a[i] - a[i-1]。这样便可以看出前缀和与差分是一种逆运算。
我们构造一个集合bn使得满足:
av = b1 + b2 + b3 + … + bv;
构造bn使得数组n称为b数组的前缀和,b数组就是a数组的差分
我们用差分能够更好的解决区间和的问题:
若给定一个数组a,若要在其中一段区间内[l,r]内加上一个数字c,我们可能第一时间会用for(int i = l ;i <= r; i++) a[i] += c;
这样时间复杂度为O(n),若区间[l,r]非常大,处理起来容易超时,因此我们用差分进行操作,s[l] += c; s[r + 1] -= c;
这样保证只在区间l到r之间加上c。
最后我们还需要还原经过加完c的数组a,直接”倒推“,我们再看式子2,得出a[i]的办法,即a[i] = s[i] + a[i -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
C++实现:
#include
using namespace std;
const int N = 100010;
int n,m;
int a[N],b[N];
void 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]);
//相当于 for(int i = 1; i <= n; i ++) b[i] = a[i] - a[i-1];
while(m -- )
{
int l, r, c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
//求原数组差分后的情况:
for(int i = 1; i <= n; i ++) b[i] += b[i - 1]; //通过b[i] = b[i] + b[i-1]分解出原数组的a[]的情况
for(int i = 1; i <= n;i ++ ) printf("%d ", b[i]);
//或者 for(int i = 1; i <= n ; i ++) a[i] = b[i]+ a[i-1];
// for(int i = 1; i <= n ; i++) printf("%d ",a[i] );
return 0;
}
差分矩阵
输入一个n行m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1, y1, x2, y2, c,其中(x1, y1)和(x2, y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数n,m,q。
接下来n行,每行包含m个整数,表示整数矩阵。
接下来q行,每行包含5个整数x1, y1, x2, y2, c,表示一个操作。
输出格式
共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1≤n,m≤1000 ,
1≤q≤100000 ,
1≤x1≤x2≤n ,
1≤y1≤y2≤m ,
−1000≤c≤1000 ,
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
#include
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
b[x1][y1] += c;
b[x2 + 1][y1] -= c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y2 + 1] += c;
}
int main()
{
scanf("%d%d%d",&n, &m, &q);
for(int i = 1; i <= n; i ++ )
{
for(int j = 1; j <= m; j ++)
scanf("%d",&a[i][j]);
}
for(int i= 1; i <= n ;i ++)
{
for(int j = 1; j <= m ; j ++)
{
insert(i, j, i, j, a[i][j]);
}
}
while(q--)
{
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(x1, y1, x2, y2, c);
}
for(int i =1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
b[i][j] += b[i - 1][j] + b[i][j -1] - b[i -1][j - 1];
}
for(int i = 1; i <= n; i ++)
{
for(int j = 1; j <= m; j ++)
printf("%d ",b[i][j]);
puts(" ");
}
return 0;
}