【AcWing_算法基础课】差分

这篇文章探讨了一种高效的算法技巧,通过差分数组和前缀和,实现在O(1)时间内完成数组中指定区间元素的增减操作。讲解了原理、两种实现方式以及其复杂度分析,适合对算法优化感兴趣的开发者阅读。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接: 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

题目大意:

将数组a处于下标在[l, r]区间内的元素加上c

思路:

首相想到的做法是遍历一遍区间,如果满足下标在[l,r]范围内就将元素加上c,这个做法的时间复杂度是O(n)的,那么有没有什么更快的算法呢?

差分与前缀合:

  • 差分和前缀合互为逆运算
    对于数组a,如果a中的元素满足a[i] = b[1] + b[2] + …… + b[i],那么称a[i]是b[i]的前缀合,数组a是数组b的前缀合数组
    此时数组b中的元素均满足 b[i] = a[i] - a[i-1],称b[i]是a[i]的差分,数组b就是数组a的差分数组

如何实现将数组a中下标在[l, r]的元素加上c?
如果将b[l] += c, 那么a在[l, n]上的元素都会加上c(定义)
那么在将b[l] += c之后,再将b[r+1] -= c就可以实现a[r+1, n]上的元素都减去c
结果就是:只有a在[l, r]上的元素加上c
在这里插入图片描述
如图所示,红色部分加上c,绿色部分减去c,结果就是只有[l, r]区间内的元素加上c

因此,
通过对a的差分数组进行操作,只需要执行2条语句,时间复杂度为O(1);
再通过求前缀合来输出a数组


C++代码:

#include <iostream>
using namespace std;

const int maxn = 1e5 + 10;
int n, m;
int a[maxn], b[maxn];

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    
    //构建差分数组b[n]
    a[0] = 0;
    for(int i = 1; i <= n; i ++ )
        b[i] = a[i] - a[i-1];
    
    //m个询问
    while(m--){
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        
        b[l] += c;
        b[r+1] -= c;
    }
    
    //对b[n]求前缀合得到最终的a[n]
    for(int i = 1; i <= n; i ++ ){
        a[i] = a[i-1] + b[i];
        printf("%d ", a[i]);
    }
    
    return 0;
}

做法二:

在求差分数组的时候,另外一种思路可以看做对[i, r]区域插入a[i]:

#include <iostream>
using namespace std;

const int maxn = 1e5 + 10;
int n, m;
int a[maxn], b[maxn];

// 插入函数
void insert(int l, int r, int c){
    b[l] += c;
    b[r+1] -= c;
}

int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    
	//用插入的方式构造查分数组b[n]
    for(int i = 1; i <= n; i ++ )
        insert(i, i, a[i]);
    
    //m个询问
    while(m--){
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        
        insert(l, r, c);
    }
    
    //对b[n]求前缀合得到最终的a[n]
    for(int i = 1; i <= n; i ++ ){
        a[i] = a[i-1] + b[i];
        printf("%d ", a[i]);
    }
    
    return 0;
}

复杂度分析:

  • 时间复杂度:O(1),差分实现的增加只需要2条语句
  • 空间复杂度:O(n),需要维护一个长度为n的差分数组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值