差分算法(用python语言实现)
一.差分
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=" ")#直接输出差分序列的前缀和就好