【数据结构】前缀和+差分

1.1前缀和概述

前缀和就是数组的前i项之和,可用于区间求和
公式:

for(int i=0;i<n;i++)
{
    if(i==0) y[i]=x[i];
    else y[i]=y[i-1]+x[i];
}

二维前缀和
二维前缀和实际上就是一个矩阵内值的和,而矩阵又可以由两个行数或列数少一的子矩阵组合后,删去重合部分再加上右下角的值来构成,也就是以下式子:
在这里插入图片描述
代码实现如下:

for(int y=0;y<n;y++)//n行
    for(int x=0;x<m;x++)//m列
    {
        if(x==0&&y==0) b[y][x]=a[y][x];//左上角的值
        else if(x==0) b[y][x]=b[y-1][x]+a[y][x];//第一列
        else if(y==0) b[y][x]=b[y][x-1]+a[y][x];//第一行
        else b[y][x]=b[y-1][x]+b[y][x-1]-b[y-1][x-1]+a[y][x];
    }

给定二点坐标求组成的矩形面积:
红色区域面积=b[x2][y2]-b[x2][y1-1]-b[x1-1][y2]+b[x1-1][y1-1]。
在这里插入图片描述
在我们剪掉这两个多出的区域时,下边的一小块被减了两次,但减两次显然是不合理的,我们应该加回来

1.2作用

前缀和是一种预处理,用于降低查询时的时间复杂度
举个例子:给定 n个整数,然后进行 m次询问,每次询问求一个区间内值的和。
如果用暴力写法,那每次询问都需要从区间左端点循环到区间右端点求和,时间复杂度较大。
这种时候就可以预先求出该数组的一维前缀和。每次询问可直接输出答案,这样时间复杂度就降到了 O(n+m) 。

2.1差分数组概述(一般用于前期多次进行数组区间修改,后续查询某个数值)

差分数组:原始数组的相邻元素之间的差值,即 d[i]=a[i]-a[i-1]
在这里插入图片描述
其实差分数组是一个辅助数组,从侧面来表示给定某一数组的变化,它的特点是在进行区间更新时只更新少量数据,如更新a数组的1-4区间都加3时,a数组先不变,差分数组b先改变,但不是全部一般是区间的头和尾部的下一个
在这里插入图片描述
待多次区间更新操作完成后,及差分数组更新完成,最后根据差分数组的性质更新数组a;代码如下

//执行多次更新区间操作
while(m--){//操作次数
	cin>>left>>right>>change;//左右端点及其变化的值
	d[left]+=change;
	d[right+1]-=change;
}

//多次更新完成后更新原始数组a
for(int i=1;i<=n;i++)
    a[i]=a[i-1]+b[i];

//求a[m]的数值
int temp=0;
for(int i=1;i<=m;i++)
    temp+ =b[i];
cout<<temp;

3.进阶(差分数组结合树状数组)

实现单点更新&&区间更新&&单点查询
原理:利用树状数组C维护差分数组B,对差分数组B进行单点更新即对原始数组A进行区间或者单点更新,对差分数组B进区间求和即对原始数组A进行单点查询;
模板代码如下:

typedef long long ll;
ll a[500001],b[500001],c[500001],n,m;  // b为差分数组,c为树状数组
ll lowbit(ll x){
	return x&(-x);
}
//单点更新
ll update(ll pos,ll cnt){
	while(pos<=n){
		c[pos]+=cnt;
		pos+=lowbit(pos);
	}
}
//区间求和 1-pos
ll getsum(ll pos){
	ll sum=0;
	while(pos>0){
		sum+=c[pos];
		pos-=lowbit(pos);
	}
	return sum;
}
 
 
int main(){
	cin>>n;  //原始数组长度n
	ll last=0,now;  
	//数组初始化
	for(ll i=1;i<=n;i++){
		cin>>now;
	    a[i]=now;
	    b[i]=now-last;
		update(i,now-last);//进行差分 
		last=now;
	}
    //单点或者区间更新
    //单点x更新2
    update(x,2);
    update(x+1,-2);
    //区间x-y更新3
    update(x,3);
    update(y+1,-3);
    //单点x查询
    cout<<getsum(x)<<endl;

	return 0;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值