差分(1)
差分是什么
我也不知道啊!
简单地说,差分数组就是↓这样的。
d[i] = a[i] -a[i - 1];
(d 是差分数组 ,a 是原数组)
(a[0]=0)
(两个数组下标从1开始)
举个栗子:
a[] = { 1,2,3,6,9,13 }
d[] = { 1,1,1,3,3,4 }
差分有什么用
我还是不知道啊!
最简单的,差分数组可以运用于区间修改&单点查询。
来道题目
N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便会骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?
简化一下题意:N次操作,将区间 [a,b] ++,N此操作后,输出每个点的值。(初始值为零)
这题就是上述的最简单的应用。
先看下代码
#include<cstdio> const int N=1e5; int c[N+10],s[N+10]; int main(){ int n; int a,b; for(int i=1;i<=n;i++){ scanf("%d%d,&a,&b); c[a]++; c[b+1]--; } for(int i=1;i<=n;i++){ s[i]=c[i]+s[i-1]; } for(int i=1;i<=n;i++){ printf("%d ",s[i]); } return 0; }
是不是很简单
现在来分析一下
现有两个命题,需证明
1.差分数组的前缀和为原数组
2.区间修改只影响差分数组中的两个点——左端点 和 右端点的下一位
留作习题答案略,读者自证不难
前缀和
关于前缀和也有很多应用,此处不再赘述。
简单介绍一下概念:
s[i]=a[1]+a[2]+...+a[i]
也就是 s[i]=s[i-1]+a[i].
证明:(虽说很简单,但也写一下吧。)
s[i]=d[1]+d[2]+d[3]+...+d[i]
=a[1]-a[0]+a[2]-a[1]+a[3]-a[2]+...+d[i]-d[i-1];
=d[i]-a[0]
=d[i]
这样,就得到了差分数组的前缀和等于原数组
修改前 | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
c[1] | c[2] | c[3] | c[4] | c[5] | c[6] | c[7] | |
修改后 | a[1] | a[2] | a[3]+k | a[4]+k | a[5]+k | a[6] | a[7] |
c[1] | c[2] | c[3]+k | c[4] | c[5]-k | c[6] | c[7] | |
下标 | 1 | 2 | 3(left) | 4 | 5 (right) | 6 | 7 |
列个表,惊喜的发现,只有 left 和right +1 两个点的值有改变
为什么呢?
这回真不写了,实在太过简单了,自己观察或者写写吧,相信不过几分钟的事。
这样子,就把对整段区间的修改,转换为了对两个节点的修改。
大大减小了时间复杂度。
在回顾一下上面的题目与代码,是不是这样的呢?
其他:比如树上差分,差分约束,就等下次见吧
(第一篇,望多多海涵)