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

前缀和 差分数组

当我们有一连串的数字的时候 我们要求这串数字中某一区间数字的合,首先想到的办法是找到这个区间的开头和结尾,但是要求多组的这样区间的和的时候,这样的“笨办法将会非常的麻烦”,占时间也占空间。
于是我们可以引入一个概念:前缀和
前缀和是某下标之前包括这个下标的数组内数字的和
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;
}
### 差分数组前缀和的定义 差分数组是一种用于高效处理区间增减的操作工具。对于一个长度为 \( n \)数组 \( A \),其对应的差分数组 \( D \) 定义如下: \[ D[i] = A[i] - A[i-1],\quad i=1,2,\dots,n; \text{其中 } A[-1]=0 \][^1]。 而前缀和则是指在一个数组中,计算从起始位置到当前位置的所有元素之的结果集合。如果有一个数组 \( B \),那么它的前缀和数组 \( P \) 可表示为: \[ P[i] = \sum_{j=0}^{i}{B[j]},\quad i=0,1,...,n-1 \][^2]。 两者之间存在着一种逆运算关系,即对差分数组执行前缀和操作能够恢复原始数组,反之亦然[^3]。 ### 异分析 尽管它们都基于数组构建并服务于特定的数据查询需求,但两者的用途截然不同: #### 应用场景对比 - **前缀和**主要用于快速获取任意子数组的总。当需要多次询问某一段连续区域内的数值累积时非常有效率[^4]。 ```cpp int prefixSum(int index){ return sum[index]; } ``` - **差分数组**则适用于频繁地向某一范围内的多个数加上同一个常量的情况。它允许我们先记录下每次变化的影响,在最终阶段一次性更新整个序列的状态。 ```cpp void applyDifferenceArray(vector<int>& diffArr,int startIdx,int endIdx,int valToAdd){ diffArr[startIdx]+=valToAdd; if(endIdx+1<diffArr.size()) diffArr[endIdx+1]-=valToAdd; } vector<int> reconstructOriginalFromDiff(const vector<int>& diffArr){ vector<int> original(diffArr.size()); original[0]=diffArr[0]; for(size_t i=1;i<original.size(); ++i){ original[i]=original[i-1]+diffArr[i]; } return original; } ``` 上述代码展示了如何利用差分技术来简化批量调整的过程,并且在必要时刻重新生成完整的数据列表以便进一步查阅或其它目的使用。 ### 总结说明 综上所述,虽然二者均涉及到了一些预处理步骤以加速后续可能重复发生的相似请求解答速度,但是由于各自侧重点有所异——前者更倾向于解决累加类问题,后者更适合应对大规模同步变动情形下的管理难题——所以在实际编程过程中应当依据具体情况合理选用合适的方法论来进行优化设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值