「学习笔记」前缀和与差分

一维前缀和#

就是一个数列前 n 项的总和
如何实现前缀和?
代码:

const int N=1e5+10;
int sum[N],a[N]; //sum[i]=a[1]+a[2]+a[3].....a[i];
for(int i=1;i<=n;i++)
{ 
    sum[i]=sum[i-1]+a[i];   
}

查询某个数或某一段区间的区间和,查询时间复杂度为 O1,预处理的时间复杂度为 On
查询代码:

scanf("%d%d",&l,&r);
printf("%d\n", sum[r]-sum[l-1]);

原理:
suml=a1+a2+a3+a4+...+al
sumr=a1+a2+a3+a4+...+al+al+1+al+2+...+ar
lr 区间的和
sumrsuml1=al+al+1+...+ar

二维前缀和#

其实跟一维的原理一样,只不过它所记录的是是左上角11到右下角ij所包含的矩阵元素的和

如图,蓝色部分是我们要求的,我们可以用目前已知的11ij11ij111i1j来求,但这里要注意,11i1j1在这里被减了2遍,所以要再加上11i1j1
所以 a(i,j)=s(i,j)s(i,j1)s(i1,j)+s(i1,j1)
因而也可以得到二维前缀和的预处理公式:
s(i,j)=a(i,j)+s(i,j1)+s(i1,j)s(i1,j1)
预处理时间复杂度为 Onm ,查询时间复杂度为 O1

一维差分#

可以看作前缀和的逆运算
给定原数组:b1,b2,b3,b4......
现在构造一个数组:a1,a2,a3,a4,a5......
我们让它们做到 ai=b1+b2+b3+b4+...+bi
可以看出,ab 数组的前缀和数组,那么反过来,b 就是 a 的差分数组
每一个 ai ,都是 b1 开始到 i 的区间和
构造:
b1=a1a0
b2=a2a1
b3=a3a2
......
差分数组有什么用?
lr 区间里的元素都加上 x
按照一般做法,

for(int i=l;i&lt;=r;++i)
{
  a[i]+=x;
}

这种做法是 On 的,数据一大,我们就承受不了这种复杂度了
这里,我们可以考虑差分
上面可以看出,差分记录的只是差,所以 bl+1br 之间不受影响,因为都是加 x ,所以差不变,但是, al+x ,那么,alal1=bl+x ,所以 bl 要加上 x (这就是为什么前面我说的是l+1r 而不是 lr),同理,ar+x ,那么,ar+1ar=br+1x
由此可以看出,我们只要在 b[l]+=x,b[r+1]-=x,就可以解决问题了,时间复杂度为 O1(忽略常数)

二维差分#

原理差不多,但相对于二维前缀和,二维差分更复杂一些
我们要始终记得,原数组 a 是差分数组 b 的前缀和数组,一旦 b 有变化,那么,a 也会随之改变
构造:

void insert(int x1,int y1,int x2,int y2,int c)
{
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
for(int i=1;i<=n;i++)
{
    for(int j=1;j<=m;j++)
    {
        insert(i,j,i,j,a[i][j]);    //构建差分数组
    }
}

上面这种构造要把 b 数组设为空,当然,也有关于二维差分操作也有直接的构造方法,公式如下:
b(i,j)=a(i,j)a(i1,j)a(i,j1)+a(i1,j1)
ijhk的矩阵的元素都加上 X
因为 a(i,j)+x ,所以 a(i,j)a(i1,j)a(i,j1)+a(i1,j1)=b(i,j)+x
同理
因为 a(h,j)+x ,所以 a(h+1,j)a(h,j)a(h+1,j1)+a(h,j1)=b(h+1,j)x
因为 a(i,k)+x ,所以 a(i,k+1)a(i1,k+1)a(i,k)+a(i1,k)=b(i,k+1)x
因为 a(h,k)+x ,所以 a(h+1,k+1)a(h,k+1)a(h+1,k)+a(h,k)=b(h+1,k+1)+x
b(i,j)b(h,k) 除去 b(i,j) 因为都 +x,所以不受影响
求和

for(int i=1;i<=n;i++)
{
    for(int j=1;j<=m;j++)
    {
        b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
    }
}

这种求和方法,b 数组每一次都在变化,而且每一次变化都标留了(+=),当然,也有其他求和版本

long long c[1000][1000];
long long x,y;
scanf("%lld%lld",&x,&y);
for(int i=1;i<=x;++i)
{
    for(int j=1;j<=y;++j)
    {
        c[x][y]+=b[i][j];
    }
}

作者:yifan0305

出处:https://www.cnblogs.com/yifan0305/p/16418891.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载时还请标明出处哟!

posted @   yi_fan0305  阅读(57)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示