差分、前缀和
差分
区间加:把数组a[l]到a[r]都加上k,这种操作称为区间加。
如果一般朴素的想法应该是下面这样的:
int a[10005],l,r,k; for(int i=l;i<=r;i++){ a[i]+=k; }
但可以发现如果是l-r非常大的话,这个操作执行的次数又很多,那时间复杂度会很高(其实是我不会算)
所以我们通过引入差分的概念,简化这个问题。
差分:差分即相邻两个数的差。我们用一个数组p存储a的差分,那么p是a的差分数组,即pi=ai-ai-1 。
这张图可以帮助更好地理解差分的概念
那么通过差分,如果继续做区间加操作的话,只需要像下面这样。
首先是查分数组的初始化:
int p[10],a[10]={0,1,2,3,90,5,6,7,8,9}; //p是a的差分数组 for(int i=1;i<=9;i++){ p[i]=a[i]-a[i-1]; cout<<p[i]<<" "; }
这段代码会输出
输出: 1 1 1 87 -85 1 1 1 1
那么我们现在就可以进行区间加操作了,代码如下:
int p[10],a[10]={0,1,2,3,90,5,6,7,8,9}; void pl(int l,int r,int k){ //操作:在[l,r]上加 k p[l]+=k,p[r+1]-=k; //注意是 r+1 !!! return; }
那么当我们要输出数组a的某一个元素要怎么办呢
很简单,通过差分数组p和原来的数组a“倒推”出a[i],代码实现如下
void get_a(){ for(int i=1;i<=9;i++){ a[i]=p[i]+a[i-1]; } }
前缀和
前缀和其实就是用一个数组s来记录数组a前i项的和,有点类似数列的求和。也可用来求区间和,如求数组a在区间[l,r]的和,就可以s[r]-s[l-1](注意要减1哦!!!!!)
我们在输入a数组时可以预处理一下,代码如下。
int a[11],s[11]; a[0]=0,s[0]=a[0]; for(int i=1;i<=10;i++){ a[i]=i*i; s[i]=s[i-1]+a[i]; cout<<a[i]<<" "; } cout<<endl; for(int i=1;i<=10;i++){ cout<<s[i]<<" "; } cout<<endl;
输出结果如下:
1 4 9 16 25 36 49 64 81 100 1 5 14 30 55 91 140 204 285 385
那么区间[l,r]的和就是下面这样:
int qjh(int l,int r){ return s[r]-s[l-1]; } cout<<qjh(3,6);
这里会输出
86