今天我们来讲一下一维差分

什么是差分呢?

其实差分和前缀和是逆运算,由定义 b[i]=a[i]-a[i-1] ;可得 $a[i] = \displaystyle\sum_^i b[j]$ ,那么就称b是a的差分数组。差分数组可以将对a数组任意区间的同一操作优化到$O(1)$

那么构造差分数组其实也挺简单的,用第i个元素减去第i-1个元素存入b[i]即可

常见的一维差分操作是令某一段区间的数同时加上一个数,我们利用定义出发很容易就能想到只要在区间起点位置加上一个数就行了,但是这样的话我们构成的新数组就是从起点开始以后的数都加上一个数,因为我们想要的只是一段区间,所以只需要在终点的下一个位置减去这个数即可

我们来看看模板吧

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

其实在我们构造一维差分数组是有一个更便捷的方法,就是将一段区间加上一个数这一操作特殊化。

我们将差分数组初始化为0,这时原数组也是0,可以看成b数组是a数组的差分数组,而a数组则是b数组的前缀和数组。那么我们需要对a数组输入数据,就可以看成是对 [i , i] 这个区间加上一个数,那么进行n次操作后我们就可以构造出一个新的差分数组了,因为后续我们需要对一段区间操作,所以利用这一性质我们可以直接合并这一操作,使其利用率更大2333

那么我们直接用模板题试一下吧

AcWing 797.差分

题目描述

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

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

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

输入格式

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

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

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

输出格式

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

数据范围

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

输入样例:

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

输出样例:

3 4 5 3 4 2

C++代码

#include <cstdio>

using namespace std;

const int N = 100010;

int a[N] , b[N];
int n , m;

void insert(int l , int r , int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

int main()
{
    scanf("%d%d" , &n , &m);
    
    //这里其实也可以不用a数组,用变量也行,但是可能后续需要用到和为了方便理解,所以用到了a数组
    for(int i = 1; i <= n; i ++) scanf("%d" , &a[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] + b[i] , printf("%d " , b[i]);

    return 0; 
}

引用

引用一下我觉得还不错的题解,我解释的可能稍微抽象点

dongwa_zzuli AcWing 797. 差分_java

z林深时见鹿 AcWing 797. 差分 【c++详细题解】

自由周某 AcWing 797. 差分

Q.E.D.


都懂一点,不是很懂的小捞仔