前缀和 && 差分

前缀和

  • 对输入数组做预处理,可以快速求出数组中某连续一段元素的和

前缀和

//前缀和一定要让下标从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;
}

拓展到二维情况 差分矩阵

posted @ 2021-04-12 21:53  呼_呼  阅读(48)  评论(0编辑  收藏  举报