C++数据结构与算法分析——前缀和与差分

前缀和

定义

假定有一数组 a [ 1 ] ∼ a [ n ] a[1] \sim a[n] a[1]a[n],前缀和 s [ i ] s[i] s[i]就是前i项的和。
例如
s [ 1 ] = a [ 1 ] s[1] = a[1] s[1]=a[1]
s [ 2 ] = a [ 1 ] + a [ 2 ] s[2] = a[1] + a[2] s[2]=a[1]+a[2]
s [ n ] = a [ 1 ] + a [ 2 ] + . . . . . . + a [ n ] s[n] = a[1] + a[2] + ...... + a[n] s[n]=a[1]+a[2]+......+a[n]

应用

由此我们可以得到,若要求得数组 a [ l ] ∼ a [ r ] a[l] \sim a[r] a[l]a[r]项的元素总和,只需要前r项的元素总和s[r]
减去前l-1项的元素总和s[l - 1]即可。

模板

// 前缀和模板
s[i] = a[1] + a[2] + a[3] + ...... + a[i];
a[l] + a[l + 1] + ... + a[r] = s[r] - s[l - 1];

例题

题目描述

输入一个长度为 n 的整数序列。

接下来再输入 m 个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

输入格式

第一行包含两个整数nm

第二行包含n个整数,表示整数数列。

接下来m行,每行包含两个整数lr,表示一个询问的区间范围。

输出格式

共 m 行,每行输出一个询问的结果。

数据范围

1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1lrn,
1 ≤ n , m ≤ 100000 1≤n,m≤100000 1n,m100000,
− 1000 ≤ 数 列 中 元 素 的 值 ≤ 1000 −1000≤数列中元素的值≤1000 10001000

输入样例:

5 3
2 1 3 6 4
1 2
1 3
2 4

输出样例:

3
6
10

题解

对于每个询问的lr,都需要返回序列的区间 a [ l ] ∼ a [ r ] a[l] \sim a[r] a[l]a[r]之和,根据模板很容易得出我们需要的代码

#include <iostream>
using namespace std;

const int N = 100010;
int n, m;
int s[N];


int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> s[i], s[i] += s[i - 1]; //求前缀和
    while (m--) {
        int l, r;
        cin >> l >> r;
        cout << s[r] - s[l - 1] << endl;
    }

    return 0;
}

注意事项

前缀和的数组从 a [ 1 ] a[1] a[1]开始是因为 s [ i ] = s [ i − 1 ] + a [ i ] s[i] = s[i -1] + a[i] s[i]=s[i1]+a[i]
假如从 a [ 0 ] a[0] a[0]开始的话 s [ 0 ] = s [ − 1 ] + a [ 0 ] s[0] = s[-1] + a[0] s[0]=s[1]+a[0]显然有问题。

差分

定义

假定有一数组 a [ 1 ] ∼ a [ n ] a[1] \sim a[n] a[1]a[n],此时我们设一个数组 b [ 1 ] ∼ b [ n ] b[1] \sim b[n] b[1]b[n],使得
a [ i ] = b [ 1 ] + b [ 2 ] + . . . + b [ i ] a[i] = b[1] + b[2] + ... + b[i] a[i]=b[1]+b[2]+...+b[i]
因此我们容易得出
b [ 1 ] = a [ 1 ] b[1] = a[1] b[1]=a[1]
b [ 2 ] = a [ 2 ] − a [ 1 ] b[2] = a[2] - a[1] b[2]=a[2]a[1]
b [ n ] = a [ n ] − a [ n − 1 ] b[n] = a[n] - a[n - 1] b[n]=a[n]a[n1]
由此我们也可以发现差分就是前缀和的逆运算

应用

差分可以解决一个区间[l,r]内同时加上或者减去同一个数的问题。
具体实现如下:
要实现a[l] ~ a[r]同时加上一个数c,我们首先从差分的定义入手
a[l - 1] = b[1] + b[2] + ... + b[l - 1]
a[l] = b[1] + b[2] + ... + b[l - 1] + b[l]
a[r] = b[1] + b[2] + ... + b[l - 1] + b[l] + b[l + 1] + ... + b[r]
a[r + 1] = b[1] + b[2] + ... + b[l - 1] + b[l] + b[l + 1] + ... + b[r] + b[r + 1]
从上面式子中我们可以发现a[l - 1]以后的元素都有b[l],而a[r + 1]以前的元素都没有b[r + 1]
因此要在a[l] ~ a[r]同时加上c,只要让b[l] + cb[r + 1] - c即可。

模板

给区间[l, r]中的每个数加上c:
B[l] += c, B[r + 1] -= c

例题

题目描述

输入一个长度为n的整数序列。

接下来输入m个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c

请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 n 和 m。

第二行包含 n 个整数,表示整数序列。

接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出格式

共一行,包含 n 个整数,表示最终序列。

数据范围

1 ≤ n , m ≤ 100000 1≤n,m≤100000 1n,m100000,
1 ≤ l ≤ r ≤ n 1≤l≤r≤n 1lrn,
− 1000 ≤ c ≤ 1000 −1000≤c≤1000 1000c1000,
− 1000 ≤ 整 数 序 列 中 元 素 的 值 ≤ 1000 −1000≤整数序列中元素的值≤1000 10001000

输入样例:

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

3 4 5 3 4 2

题解

有了上面的分析,这题只要把b[1] ~ b[n]求出来,再套用模板即可AC。

#include<iostream>
using namespace std;

const int N = 1e5+10;
int a[N],b[N];

int main(){
    int n,m,tmp;
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= n; i++){
        scanf("%d",&a[i]);
        b[i] = a[i] - a[i - 1];
    }
    
    while(m --){
        int l,r,c;
        scanf("%d%d%d",&l,&r,&c);
        b[l] += c;
        b[r + 1] -= c;
    }
    
    int k = 0;
    for(int i = 1; i <= n; i++){
        k += b[i];
        printf("%d ",k);
    }
    
    return 0;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L_Hygen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值