前缀和 差分数组(来自小白的理解)

前缀和 差分数组

当我们有一连串的数字的时候 我们要求这串数字中某一区间数字的合,首先想到的办法是找到这个区间的开头和结尾,但是要求多组的这样区间的和的时候,这样的“笨办法将会非常的麻烦”,占时间也占空间。
于是我们可以引入一个概念:前缀和
前缀和是某下标之前包括这个下标的数组内数字的和
a[]为原数组,b[]为前缀和数组
则可得b[]的递推式为:

b[i]=b[i-1]+a[i]

说简单就是有一个数组,它每一位是包括当前这一位与之前所有位的和。

举个栗子

a[0] a[1] a[2] a[3] a[4] a[5]
  1    2    3    4    5    6
b[0] b[1] b[2] b[3] b[4] b[5]
  1    3    6    10   15   21

其中b[0]=a[0]
b[1]=b[0]+a[1]
b[2]=b[1]+a[2]

前缀和的作用

可以通过一次的运算求出这个数组中一个区间的和
比如我们要求出a[1]到a[4]的和(包括这两位)时可以选择用一个循环从1遍历到4依次相加,结果为14
如果用前缀和只需要计算出  b[4] -b[0]=14的结果就好了

由此我们可以得出当要求某个区间 i 到 j 的所有数字之和时,只需计算这个数组的前缀和数组

ans=b[j]-b[i-1]

大大简化了操作,看了没懂的可以试试手推。
这个东西用法也蛮多的,我这里就讲讲思想就好了,再难的我也说不出来了。

差分数组

为啥把这两个东西放在一块说呢,因为它俩貌似是个逆运算。
差分数组的用途其实是修改一个区间内的所有值
咋算嘞?
我们假设a[]为原始数组b[]为差分数组则:
b[0]=a[0]
b[i]=a[i]-a[i-1]

说白了就是原来数组相邻两项的差

01234567
a[]134578911
b[]12112112

假设我们要在[2,5]这个区间加上5
在[1,6]这个区间上减3
我们可以通过循环来执行
可是当数据过多,区间过大的时候,这个操作也将会非常耗时耗力耗内存。

然后差分数组他就闪亮的的登场了!!!
差分有个神奇的地方,就是当你给原来的数组区间加上或减去同一个数字的时候,他们的差分是不会变的

01234567
a[]134+55+57+58+5911
b[]12112112

所以如果我们要修改这个区间的值的时候,就可以通过修改他们的差分数组来修改
假设我们要给[L,R]区间上加x,由于在这个区间上加的数字是相同的,所以在这个区间的差分是不变的,只需要修改两个短点的值就好了。

b[L]=a[L]-a[L-1]
=>a[L]=b[L]+a[L-1]

所以要让 a[L] 加上x 就给b[L]加上 x 就好了
因为:

 a[i]=b[i]+a[i-1]
 =b[i]+b[i-1]+a[i-2].....

以此类推,但是给b[L]加了 x 后 L 之后的每个a[i] 都会变大 x
所以在区间的右端要减去 x 不让区间外的数字改变大小,但是a[R]也要改变,所以只能在b[R+1]的地方减去 x
总结来说就是当要改变区间[L,R]中数字的大小的时候,只需要对它的差分数组中的

                    {b[L]+x,b[R+1]-x}

来一道差分组的题

差分数组,经典例题

#include <cstring>
#include <iostream>
using namespace std;
int a[100009];
int b[100009];
int main() {
    int N;
    while (cin >> N && N != 0) {
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        int L, R;
        for (int i = 1; i <= N; i++) {  //对区间内的每个都加 1
            cin >> L >> R;
            b[L] += 1;
            b[R + 1] -= 1;
        }
        for (int i = 1; i <= N; i++) {  //转换为原数组
            a[i] = a[i - 1] + b[i];
        }
        for (int i = 1; i <= N; i++) {
            if (i != 1) cout << " ";
            cout << a[i];
        }
        cout << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值