1、前缀和
表示:Si = a1 +a2 + a3 +...+ai下标一定从一开始
求Si : Si = Si-1 + ai
可以快速求出[l, r]一段数列的和
为了在边界可以统一公式,定义S0 = 0
一维前缀和
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
for(int i == 1; i <= n; i ++) s[i] = s[i - 1] + a[i];//前缀和的求解
printf("%d\n", s[r] - s[l]);
二维前缀和
/*
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
*/
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);//读入a[i]
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
printf("%\n", s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1][y1 - 1]);//求得子矩阵的和
2、差分
一维差分
首先给定一个原数组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数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。
构造一个差分:
例如:
b1 = a1
b2 = a2 - a1
b3 = a3 - a2
但是在实际代码编写上只构造a, b的全为0的数组也可表示上述规则,这样我们就只需要考虑更新规则在构造是利用边界insert(i, i, a[])
//给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
void insert(int l, int r, int c)
{
b[l] += c;
b[r + 1] -= c;
}
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);//输入需要差分的数列
for(int i = 1; i <= n; i++) insert(i, i, a[i]);//利用边界将b[i]构造出来
//当数列中的一段需要整体的加上一个数是
scanf("%d%d%d", &l, &r, &c);//输入需要整体加的起始地址,终止地址,和需要加的数
insert(l, r, c)
for(int i = 1; i <= n; i++) b[i] += b[i - 1];//因为a[i] = b[1] + ..b[i],每个b[i] 就都对应区间加了之后的数列了
for(int i = 1; i <= n; i++) prinf("%d", b[i]);//输出加完的数列
二维差分
a[][]
数组是b[][]
数组的前缀和数组,那么b[][]
是a[][]
的差分数组
原数组: a[i][j]
我们去构造差分数组: b[i][j]
使得a
数组中a[i][j]
是b
数组左上角(1,1)
到右下角(i,j)
所包围矩形元素的和。
始终要记得,a数组是b数组的前缀和数组,比如对b
数组的b[i][j]
的修改,会影响到a
数组中从a[i][j]
及往后的每一个数。
假定我们已经构造好了b
数组,类比一维差分,我们执行以下操作
来使被选中的子矩阵中的每个元素的值加上c
/*给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c*/
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;
}
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);
insert(i, j, i, j, a[i][j]);//构造b[][]差分
}
}
insert(x1, y1, x2, y2, c)//将(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];//每个b[i]都加上一个做正方形对应了加完c后的a[i]的数列
}
}
部分笔记学习于前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)
以上学习自AcWing