差分与前缀和是一对互逆的操作,常用于处理区间问题。差分法解决区间加减问题,前缀和解决区间求和问题。
差分
当某一个数组要在很多不确定的区间,加上同一个数,如果对每个都进行加法操作的话。那么时间复杂度会是O(mn)。【数组长度m 操作次数n】
这时可以采用差分法,将数组拆分,构造出一个新的拆分数组,通过对数组区间的端点进行加减操作,最后将数组合并,就能完成原来的操作。
特点
1)对区间的加减操作->对端点的操作
2)时间复杂度O(n)【实际上是O(m+n)】。
3) 用于维护区间的增减,但不能维护乘除。
4)差分后的序列比原来的数组序列多一个数。
原理
根据这一特性,对差分后的数组进行 b[L]+=x; b[R]-=x 就能转化为 区间a[L,R-1]+x
模板
用两个数组
//读入原始数据n,m,a
cin>>n>>m;
原始数组 a[]
差分数组 b[]
for(int i=1;i<=n;i++) cin>>a[i];
//差分
for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1];
//m次区间操作
while(m--)
{
cin>>l>>r>>value;
b[l]+=value;
b[r+1]-=value;
}
//前缀和还原
for(int i=1;i<=n;i++) a[i]=b[i]+a[i-1];
只用一个数组
减少空间占用
//读入原始数据n,m,a
cin>>n>>m;
原始数组 a[]
for(int i=1;i<=n;i++) cin>>a[i];
//差分
for(int i=n;i>=2;i--) a[i]=a[i]-a[i-1];
//m次区间操作
while(m--)
{
cin>>l>>r>>value;
a[l]+=value;
a[r+1]-=value;
}
//前缀和还原
for(int i=2;i<=n;i++) a[i]=a[i]+a[i-1];
题目
大学里的树木要打药
模板题
#include<bits/stdc++.h>
using namespace std;
#define maxsize 1000010
int a[maxsize],b[maxsize];
int main()
{
int n,m;
cin>>n>>m;
//初始都是0 不用循环求b
while(m--)
{
int l,r,value;
cin>>l>>r>>value;
l++; r++; //题目中的编号从0开始
b[l]+=value;
b[r+1]-=value;
}
//还原
int sum=0;
for(int i=1;i<=n;i++){
{
a[i]=b[i]+a[i-1];
sum+=a[i];
}
cout<<sum;
return 0;
}
前缀和
前缀和就是某序列的前n项和。当对于某一数组区间进行多次询问,求区间和时,采用前缀和数组,就可以把对区间的访问转化为对区间端点的访问。
特点
1)对区间的求和操作->对端点值的减法操作
2)时间复杂度O(n)【实际上是O(m+n)】。
3) 数组存放要从1开始。
4)前缀和的序列比原来的数组序列多一个数。
基本思路
sum[i]=a[i]+sum[i-1]
前缀和数组 sum[R]-sum[L] 就能转化为 区间a[L+1,R]内a[i]的和
int a[];
int max[];
cin>>n>>m;
for(int i=0;i<n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
输入m个区间,计算结果
int L,R;
while(M--)
{
cin>>L>>R;
int ans=sum[R]-sum[L-1];
cout<<ans;
}
题目
大学里的树木要维护
模板题
#include<bits/stdc++.h>
using namespace std;
#define maxsize 100010
int a[maxsize],sum[maxsize],ans[maxsize];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum[i]=sum[i-1]+a[i];
}
int j=0;
while(m--)
{
int L,R;
cin>>L>>R;
ans[j]=sum[R]-sum[L-1];
j++;
}
for(int i=0;i<j;i++) cout<<ans[i]<<endl;
return 0;
}