前缀和 && 差分
前缀和
- 对输入数组做预处理,可以快速求出数组中某连续一段元素的和
//前缀和一定要让下标从1开始,便于定义s[0];
//定义s[0]是为了方便处理边界情况,类似于“哨兵”技巧的作用,统一用一套代码覆盖所有情况,避免特判
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int a[N], s[N];
int main() {
//ios::sync_with_stdio(false);
//加上面这句话后cin不需要与标准输入输出同步,从而提高输入的速度,加上这句话之后不能使用scanf
scanf("%d%d", &n,&m);
for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
for(int i = 1; i <= n; i ++ ) s[i] = s[i-1] + a[i];
while( m -- ) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
前缀和技巧可以拓展到二维情况 子矩阵的和
差分
- 差分是前缀和的逆运算
- 已知a数组,构造b数组,使得a是b的前缀和
//差分的作用:对差分数组求前缀和,可以在O(n)时间内求得原数组
//在O(1)时间内给原数组某一个固定区间内的数全部加上一个值
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N];
//给一个区间内的每个数增加c,这个函数也可以用来构造差分数组
//在复原原数组(求前缀和)时,b[l]和b[r + 1]会作用于a[l]及之后的所有数,但是a[r + 1]之后的效果会被抵消
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]);
//开始时假设a中元素都为0,则对应的差分数组b中的元素都为零
//下面这一步依次将a中的元素插入假想的初始元素全为零的数组中(比如将原数组的第i个元素由0变为a[i],相当于向差分数组的[i, i]区间插入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);
}
//求一下原来的数组(求前缀和)
for(int i = 1; i <= n; i ++ ) b[i] += b[i - 1];
for(int i = 1; i <= n; i ++ ) printf("%d ", b[i]);
return 0;
}
拓展到二维情况 差分矩阵