差分
类似于数学中的求导和积分,差分可以看成前缀和的逆运算。
差分数组:
首先给定一个原数组a
:a[1], a[2], a[3],,,,,, a[n];
然后我们构造一个数组b
: b[1] ,b[2] , b[3],,,,,, b[i];
使得 a[i] = b[1] + b[2 ]+ b[3] +,,,,,, + b[i]
也就是说,a
数组是b
数组的前缀和数组,反过来我们把b
数组叫做a
数组的差分数组。换句话说,每一个a[i]
都是b
数组中从头开始的一段区间和。
考虑如何构造差分b
数组?
最为直接的方法
如下:
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]
;
图示:
我们只要有b
数组,通过前缀和运算,就可以在O(n)
的时间内得到a
数组 。
知道了差分数组有什么用呢? 别着急,慢慢往下看。
话说有这么一个问题:
给定区间[l ,r ]
,让我们把a
数组中的[ l, r]
区间中的每一个数都加上c
,即 a[l] + c , a[l+1] + c , a[l+2] + c ,,,,,, a[r] + c
;
暴力做法是for
循环l
到r
区间,时间复杂度O(n)
,如果我们需要对原数组执行m
次这样的操作,时间复杂度就会变成O(n*m)
。有没有更高效的做法吗? 考虑差分做法。
始终要记得,a数组是b数组的前缀和数组,比如对b
数组的b[i]
的修改,会影响到a
数组中从a[i]
及往后的每一个数。
首先让差分b
数组中的 b[l] + c
,a
数组变成 a[l] + c ,a[l+1] + c,,,,,, a[n] + c
;
然后我们打个补丁,b[r+1] - c
, a
数组变成 a[r+1] - c,a[r+2] - c,,,,,,,a[n] - c
;
为啥还要打个补丁?
我们画个图理解一下这个公式的由来:
b[l] + c
,效果使得a
数组中 a[l]
及以后的数都加上了c
(红色部分),但我们只要求l
到r
区间加上c
, 因此还需要执行 b[r+1] - c
,让a
数组中a[r+1]
及往后的区间再减去c
(绿色部分),这样对于a[r]
以后区间的数相当于没有发生改变。
因此我们得出一维差分结论:给a
数组中的[ l, r]
区间中的每一个数都加上c
,只需对差分数组b
做 b[l] + = c
, b[r+1] - = c
。时间复杂度为O(1)
, 大大提高了效率。
总结:
AC代码
//差分 时间复杂度 o(m)
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
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; //将序列中[l, r]之间的每个数都加上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;
}
感谢,比y总的容易理解(对于新手)
雀氏
同感
是这样的
比y总讲的好,一看就理解了
同感
太有实力啦
借楼
n,p = map(int,input().split())
q = list(map(int,input().split()))
b = [0]*(n+2)
def insert(l,r,c):
global b
b[l] = b[l] +c
b[r+1] = b[r+1] - c
for i in range(n):
q[i] = int(q[i])
insert(i+1,i+1,q[i])
for i in range(p):
l,r,c = map(int,input().split())
insert(l,r,c)
cnt = 0
min1 = 101
for i in range(1,n+1):
cnt = cnt + b[i]
if cnt<min1:
min1 = cnt
print(min1)
洛谷上的题同差分数据为6e6,爆内存,c++可以但是python不行,我该怎么优化,求大佬帮忙
确实比y总的容易理解一些
随便记录点,希望对看不懂的有帮助
豁然开朗
大佬好厉害,想问一下 a[N]写成全局变量,是因为会自动初始化为0 就不用初始化a[0]为0了嘛
额
一般信竞选手都会把大部分数组设为全局定义
不过,你说得对。
谢谢大佬~
简化了一下代码,减少了一个数组
差分跟多米诺骨牌一样,前一个和跟后一个有联系才能一个一个接着往前推
赞
学习总结了一、二维前缀和差分,希望对你有帮助~
https://blog.csdn.net/m0_74215326/article/details/129620912?spm=1001.2014.3001.5502
tql
谢谢太牛了!
感谢感谢
这下通透了!!!
a[i] = b[i] + a[i - 1]; //前缀和运算
假设L=1,r=10
看不懂这一步的:
i=1时 a[1]=b[1]+a[0]=a[1]+c
i=2时 a[2]=b[2]+a[1]=(a[2]-a[1])+a[1]+c=a[2]+c
老哥怎么发图片的
懂了哈哈
$\color{Magenta}{hhh,我也才学的makedown语言}$
这是什么
感谢林子哥 解决了我2h的困扰
题解写得真好!!
大佬,你给的代码运行后输出的结果中最后一位数与标准答案不一样呢,
大佬在人间
Orz
提醒一下,如果开辟的是大小为n + 1的动态数组的话,在
while (m–)
{
scanf(“%d%d%d”, &l, &r, &c);
b[l] += c; //将序列中[l, r]之间的每个数都加上c
b[r + 1] -= c;
}
这里面,要对b[r + 1] -= c;进行判断if(r < n),否则会出现数组越界
太赞了。y总第第二个for循环我硬是没看懂啥意思,你的代码我看下就理解了!!!!
太优雅了