AcWing 797. 差分

差分

定义

给定一个原数组 \(a\),构造一个数组 \(b\),使得 \(a_i = b_1 + b_2 + b_3 + … + b_i\)
也就是说,\(a\)\(b\)前缀和数组,我们称 \(b\)\(a\)差分数组
换句话说,差分是前缀和的逆运算

作用

\(O(1)\) 的时间复杂度内将序列中 \([l, r]\) 的一段区间内所有数加上 \(c\)(减法也一样)。

更具体些

构造差分数组

其实很简单:

//令 a[0] = 0

b[1] = a[1] - a[0];
b[2] = a[2] - a[1];
b[3] - a[3] - a[2];
...
b[n] = a[n] - a[n - 1];

重点!如何实现在 \(O(1)\) 的时间复杂度内将序列中 \([l, r]\) 的一段区间内所有数加上/减去 \(c\)

先放公式:

//这里是加上c的情况

b[l] += c;
b[r + 1] -= c;
为什么这样做?
  1. b[l] += c;:前面定义说了,\(a\)\(b\)前缀和数组,所以只要改变 \(b_l\)\(a_l \sim a_n\) 都会跟着改变;
  2. b[r + 1] -= c;:因为我们只需要改变 \(a_l \sim a_r\)\(a_{r + 1} \sim a_n\) 是多改变的,所以要让这一段 \(-c\)

个人感觉有点类似于“后缀和”思想。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 100010;

int a[N], b[N];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ )
    {
        scanf("%d", &a[i]);
        b[i] = a[i] - a[i - 1]; // 预处理出差分数组
    }
    int l, r, c;
    while (m -- )
    {
        scanf("%d%d%d", &l, &r, &c);
        // 按上面的公式执行
        b[l] += c;
        b[r + 1] -= c;
    }
    for (int i = 1; i <= n; i ++ )
    {
        a[i] = b[i] + a[i - 1]; // 差分数组的前缀和是使原数组
        printf("%d ", a[i]);
    }
    return 0;
}

另一种构造方式

上面讲了将 \([l, r]\) 区间内的所有数加 \(c\) 的公式:

b[l] += c;
b[r + 1] -= c;

假设开始时 \(a\) 数组都是 \(0\),所以 \(b\) 数组也都是 \(0\)

如果我们要把 \(a_i\) 初始时设成 \(c\),其实可以看做在 \([i, i]\) 的这段区间中的数加 \(c\),代码如下:

b[i] += c;
b[i + 1] -= c;

第二种构造方式的代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

const int N = 100010;
int a[N], b[N];

// 这里把[l, r]区间加c的操作封装成一个函数
void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

int main()
{
    int n, m, l, r, c;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ ) insert(i, i, a[i]); // 初始化差分数组
    while (m -- )
    {
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }
    for (int i = 2; i <= n; i ++ ) b[i] += b[i - 1]; // b的前缀和就是a数组
    for (int i = 1; i <= n; i ++ ) printf("%d ", b[i]);
    return 0;
}
posted @ 2022-08-04 17:10  FXT1110011010OI  阅读(39)  评论(0编辑  收藏  举报