前言
记录2个简单的算法及其模板,前缀和与差分。
前缀和:能够在
O
(
1
)
O(1)
O(1)时间复杂度内求出数组某一段的和
差分:能够在
O
(
1
)
O(1)
O(1)时间复杂度内给数组某一段加上同一个数(或者减去同一个数)
前缀和
可以在 O ( 1 ) O(1) O(1)时间内求出一段连续的子数组的和 s u m a , b = s u m b − s u m a − 1 sum_{a,b} = sum_b - sum_{a-1} suma,b=sumb−suma−1, s u m a , b sum_{a,b} suma,b表示求数组下标从a到b的和。
模板
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int nums[N], sum[N];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) scanf("%d", &nums[i]);
for (int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + nums[i];
while (m --) {
int l, r;
cin >> l >> r;
printf("%d\n", sum[r] - sum[l - 1]);
}
return 0;
}
二维前缀和
可以在 O ( 1 ) O(1) O(1)时间内求一个子矩阵的和
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int sum[N][N];
int n, m, q;
int main() {
cin >> n >> m >> q;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
scanf("%d", &sum[i][j]);
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++) {
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
while (q --) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int ans = sum[x2][y2] - sum[x2][y1 - 1] - sum[x1 - 1][y2] + sum[x1 - 1][y1 - 1];
printf("%d\n", ans);
}
return 0;
}
差分
差分的定义
存在数组
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an
构造数组
b
1
,
b
2
,
.
.
.
,
b
n
b_1,b_2,...,b_n
b1,b2,...,bn
使得
a
i
=
b
1
+
b
2
+
.
.
.
+
b
i
a_i=b_1+b_2+...+b_i
ai=b1+b2+...+bi
此时
b
b
b数组称为
a
a
a数组的差分,
a
a
a数组称为
b
b
b数组的前缀和
可以很简单的看出,如果我们想让 a a a数组的 a 3 a_3 a3至 a 6 a_6 a6加上一个数 x x x,我们可以让 b 3 b_3 b3加上 x x x,由于 a i = b 1 + b 2 + . . . + b i a_i=b_1+b_2+...+b_i ai=b1+b2+...+bi,所以 b 3 b_3 b3到 b n b_n bn的所有数都会增加 x x x,然后我们让 b 7 b_7 b7减去 x x x,这样就只有 a 3 a_3 a3至 a 6 a_6 a6加上一个数 x x x。能在 O ( 1 ) O(1) O(1)的时间复杂度内完成。
例题及模板
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int b[N];
// 差分操作:在[l, r]区间的数全部加上c
void insert(int l, int r, int c) {
b[l] += c;
b[r + 1] -= c;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i ++) {
int x;
cin >> x;
insert(i, i, x);
}
while (m --) {
int l, r, c;
cin >> l >> r >> c;
insert(l, r, c);
}
for (int i = 1; i <= n; i ++) b[i] += b[i - 1], printf("%d ", b[i]);
return 0;
}
二维差分
和一维差分类似,二维差分是给一个矩阵中的某个矩形块同时加上某个数
c
c
c
AcWing800
#include <iostream>
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;
}