以一个例子引入差分
定义一个arr数组,进行m次操作[L,R]区间内➕v
//如[2,4]+5,[1,3]+2,[0,2]-3
arr[5]={1,3,7,5,2};
//计算过程1,3,12,10,7
//1,5,14,12,7
//-2,2,11,12,7
//很显然,我们能算出最终结果-2,2,11,12,7
//时间复杂度为O(m*n),很显然这个时间复杂度不太行,位了优化这种算法,我们引入差分
在上述的运算方法中,我们可以看到我们运算过程中存了很多中间值,但是我们的目标只是求得arr数组,所以有没有什么好的方法能让我们直接求得arr数组而且并不存入如此多的中间值呢?
差分
arr 1 3 7 5 2
同时我们定义一个差分数组
dif 1 2 4 -2 -3
通过观察我们可以发现dif[i]是由arr[i]-arr[i-1]得到的
我们再来求一求dif数组的前缀和数组sumd
sumd 1 3 7 5 2
我们可以发现dif的前缀和数组就是arr数组
差分标记
//实现区间修改:如何实现[L,R]+v
//给出结论
[L,R]+v<=>dif[L]+v,dif[R+1]-v
//例:[2,4]+5<=>d[2]+5
//[1,3]+3<=>d[1]+2,d[4]-2
//[0,2]-3<=>dif[0]-3,dif[3]+3
arr数组1 3 7 5 2
改变后的dif数组
-2,4,9,1,-5
再求差分数组的前缀和数组sumd=-2 2 11 12 7
综上,我们可以把差分标记分成两步
- [L,R]+v<=>dif[L]+v,dif[R]-v
- 求dif数组的前缀和数组sumd
注:这种做法适用于多次操作单次询问
原理讲解
dif 0 1 0 0 0 0 0 0
定义一个sumd数组(dif前缀和数组)
如果我们在d[L]+V,等同于sumd在[L,n-1]都加了一个V
如果我们在d[R+1]-V,等同于sumd[R+1,n-1]都减了一个V
两个都进行后,d[R+1,n-1]实现了+V-V,保证不变,从而实现区间内加减
实现代码
#include <iostream>
using namespace std;
//差分数组开多一位,防止数组越界
int d[6]={0};//并不需要求出差分数组,实现区间内加减后,直接与arr数组相加
void add(int l,int r,int v)
{
d[l]+v;
d[r+1]-v;
}
int main()
{
int arr[5]={1,3,7,5,2};
//自己使用时可以改成循环
add(2,4,5);
add(1,3,2);
add(0,2,-3);
for(int i=1;i<5;i++)
{
d[i]+=d[i-1];//对d数组进行前缀和
}
//为防止多次操作,将d数组全部清零
memset(d,0,sizeof(0));
for(int i=0;i<5;i++)
{
arr[i]+=d[i];
cout<<arr[i]<<“ “;
}
return 0;
}
时间复杂度变为O(m+n)
如果有错误希望大佬批评指正,出自刚刚接触算法的小白