一.差分
1.介绍
一般地,差分主要用于多次将某个序列的某一段加上或减去某一特定值。相对于一个元素一个元素地加,大大减少了时间复杂度。
2.定义
差分可以见简单看成序列中每一个元素与其前一个元素的差
3.差分与前缀和
一般地,我们认为原序列就是差分序列的前缀和,所以把差分看做前缀和的逆运算
二.一维差分
1.定义
一维差分是指给定一个长度为n的序列a,要求支持操作pro(l,r,c)表示对a[l]~a[r]区间上的每一个值都加上或减去常数c,并求修改后的序列a。(注:我们是借助差分这个工具来对原序列的某一段进行改变,所以最终的结果还是要反映到原序列上的)
2.作用
可以将对a数组任意区间的同一操作优化到O(1)。
举个栗子:
如果我想对如下序列的子序列2~6的这一段数据上加上3,为4~8这一段的数据都加上2
l=[0,2,3,5,6,7,7,8,9,10,1,2]
l=[0,2,3,5,6,7,7,8,9,10,1,2]
for i in range(2,7):
l[i]+=3
for i in range(4,9):
l[i]+=2
for i in range(12):
print(l[i])
#在这里我们只要求将给两段上加上了特定的值
#但是如果我们要求给n个子序列上加上特定的值 并且每个子序列的中的元素都不小于m
#那么我们就要遍历长度不小于的m序列 并且要遍历n次 时间复杂度为O(m*n) 就会特别耗时
但是如果用差分的话就会简便很多:
def insert(b,l,r,c):
b[l]+=c
b[r+1]-=c
if __name__ == "__main__":
l=[0,2,3,5,6,7,7,8,9,10,1,2]
l,r,c=map(int,input().split())
insert(b,2,6,3)#如果我子序列中有m个元素 那么这里直接从O(m)降到了O(1)
insert(b,4,8,2)
for i in range(1,12):
b[i]+=b[i-1]
for i in range(1,12):
a[i]+=b[i]
print(a[i],end=' ')
3.两种模板
1.先构建原序列差分数组,然后直接在差分数组上进行操作,最后直接求出此差分数组的前缀和,便是最后的经过几波操作后的结果序列。
2.开始将差分数组都赋值为0,然后对差分数组进行操作,最后将差分数组的前缀和与原数组相加便是最后的结果。
4.实战演练
用第一个模板写
def insert(b,l,r,c):
b[l]+=c
b[r+1]-=c
if __name__ == "__main__":
n,m=map(int,input().split())
a=[0]*(n+10)#元序列
b=[0]*(n+10)#差分序列
nums=list(map(int,input().split()))
for index,val in enumerate(nums):#这里的enumerate函数可以直接罗列出此列表中的下标和元素
a[index+1]=val
for i in range(m):
l,r,c=map(int,input().split())
insert(b,l,r,c)
for i in range(1,n+1):
b[i]+=b[i-1]#求差分序列的前缀和
for i in range(1,n+1):
a[i]+=b[i]#再将两个序列相加 将差分序列的作用反映到原序列上
print(a[i],end=' ')
用第二个模板写
def insert(b, l, r, c):
b[l] += c
b[r+1] -= c
if __name__ == "__main__":
n, m = map(int, input().split())
a = [0] * (n + 10)
b = [0] * (n + 10) #
nums = list(map(int, input().split()))
for index, val in enumerate(nums):
a[index+1] = val
for i in range(1, n+1):
insert(b, i, i, a[i])#构造差分序列
while m > 0:
m -= 1
l, r, c = map(int, input().split())
insert(b, l, r, c)
for i in range(1, n+1):
b[i] += b[i-1]
print(b[i],end=" ")#直接输出差分序列的前缀和就好