神机百炼1.13-一维差分

本文详细介绍了如何利用一维差分技巧高效地处理给定整数序列的区间修改问题。通过差分数组和前缀和的关系,将操作次数从O(mn)降至O(2),并提供了求差分公式、修改步骤和代码模板。重点解析了差分算法的原理、误区及适用场景,适合算法基础扎实的学习者阅读。
摘要由CSDN通过智能技术生成

一维差分导图

食用指南:

对该算法程序编写以及踩坑点很熟悉的同学可以直接跳转到代码模板查看完整代码
只有基础算法的题目会有关于该算法的原理,实现步骤,代码注意点,代码模板,代码误区的讲解
非基础算法的题目只有题目分析,代码实现,代码误区
从这篇算法开始,精炼了算法内容,减少了冗余解释

题目描述:

  • 输入一个长度为 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

  • 题目来源:https://www.acwing.com/problem/content/799/

题目分析:

  • 给定一个序列,多次统一修改序列片段,只要求输出最终序列
  • 暴力:
    m次操作,每次遍历l到r的元素为其加c
    最坏时间复杂度:O(mn),当每次l=0,r=n-1时
    m最大十万,n最大十万,mn最大百亿,比拼多多补贴还大,严重超时
  • 一维差分:
    我们已经学习过了一维前缀和(传送门),了解了前缀和的打表公式,差分是这个公式的高级应用
    非常适合解决多次加减修改,只输出最终结果的题目
    下面介绍差分,注意理解差分的时间复杂度是O(n)

算法原理:

含义:

  • 差分是什么?
    已知前缀和数组s[] = {1, 3, 6, 10, 15};
    则原数组arr[] = {1, 2, 3, 4, 5};
    原数组arr[] 称为 前缀和数组s[] 的差分数组
  • 差分数组对前缀和数组的影响:
    现在我们将差分数组的arr[1]+1,之后前缀和数组变为s[] = {1, 4, 7, 11, 16};
    再将差分数组的arr[4]-1,则前缀和数组变为:s[] = {1, 4, 7, 11, 15};
    我们改动了差分数组的两个值arr[1] 和 arr[4],影响了前缀和数组的s[1] 到 s[3]的区间

作用:

  • 差分数组改动一点,前缀和数组改动一个区间
  • 题目要求:改动l - r的区间
    现在将题目数组视为前缀和数组,求得其差分数组之后,化区间变化为两点变化,时间复杂度大大降低,从O(n)到O(2)
  • 解题只需两步:1,求差分数组 2,改动差分数组两点 3,求前缀和还原回原数组

求差分公式:

法一:打表
  • 已知前缀和打表公式:s[i] = s[i-1] + arr[i];
  • 则差分打表公式:arr[i] = s[i] - s[i-1];
  • 当然求差分是个过程,最终目的是改动差分两点后还原回前缀和数组(已经将题目数组视作前缀和数组)
法二:插入
  • 差分数组的改动影响前缀和数组的结果
  • 已知前缀和数组中arr[3] = 4;
    可以想象这个4也是由差分插入的:
    原本s[i]和arr[i]均为0
    后来改动arr[3]+=4;arr[4]-=-4;
    则前缀和数组中只有s[3]受波动影响变为4,s[4]开始又是0
  • 所以已知前缀和数组中一点s[i],可以还原到差分数组对应两点arr[i] & arr[i+1]
  • 插入公式:arr[i] += s[i]; & arr[i+1] -= s[i];

差分区间波动公式:

  • 欲将前缀和数组中l 到 r 的每一点都+c
    差分波动示意图
    需要修改差分数组中两点:arr[l] += c; & arr[r+1] -= c;
  • 注意原数组加减范围是l & r,差分数组修改两点是l & r+1

存储形式:

  • 一维数组即可,为了方便求差分,差分数组和前缀和数组仍然从1开始存储

写作步骤:

3步

1. 由原前缀和数组求差分数组
2. 修改差分数组中两点
3. 求改动后的前缀和数组

代码模板:

const int N = 100010;
int arr[N];
int s[N];
void insert(int l, int r, int c){
	arr[l] += c;
	//注意点3:从r+1开始修剪
	arr[r+1] -= c;
}
int main(){
	int n=0, m=0;
	//注意点1:求题目数组的差分,则题目数组是前缀和数组
	//注意点2:下标都是从1开始
	for(int i=1; i<=n; i++)	cin>> s[i];
	for(int i=1; i<=n; i++) insert(i,i,s[i]);
	/*也可以打表出差分数组
	for(int i=1; i<=n; i++)	arr[i] = s[i] - s[i-1];
	*/
	for(int i=0; i<m; i++){
		int l = 0, r = 0, c = 0;
		cin >>l >>r >>c;
		insert(l, r, c);
	}
	for(int i=1; i<=n; i++)	s[i] = s[i-1]+arr[i];
	/*差分数组之后不用了,可以直接用差分数组求最终结果数组
	for(int i=1; i<=n; i++)	arr[i] += arr[i-1];
	*/
	for(int i=1; i<=n; i++) cout<<s[i];
}

代码误区:

1. 为什么输入数组是前缀和数组,而不是差分数组?

  • 题目输入一个数组后,要求统一改动数组区域
  • 差分数组改动两点后,前缀和数组改动一个区域
  • 所以为了降低时间复杂度,我们将输入数组视作前缀和数组,求其差分数组,对差分数组进行两点修改后,累加得到最终前缀和数组

2. 求差分数组有几种方法?

法一:差分打表公式:
  • arr[i] = s[i] - s[i-1];
法二:差分波动公式/插入法:
  • 由一点s[i],可以推测arr[i]和arr[i+1]
  • 由全部的s[],可以确定全部arr[]
  • 已知s[i] = a; 则arr[i] += a; arr[i+1]-=a;

3. 已知差分数组有几种方法求前缀和数组?

法一:前缀和打表公式:
  • s[i] = s[i-1] + arr[i];
法二:差分迭代:
  • arr[i] += arr[i-1];

4. 差分法适用的运算:

  • 本题是加减法
  • 换一个乘除,开方,乘方就不可
  • 因为前缀和和差分只有加减线性关系

本篇感想:

  • 今天的第一篇博客下午三点才出炉,焯!
  • 快了,马上就要到练气境的中期了,坚持
  • 看完本篇博客,恭喜已登《练气境-初期》
    练气期初阶
    距离登仙境不远了,加油 登仙境初期
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

starnight531

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

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

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

打赏作者

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

抵扣说明:

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

余额充值