前缀和,差分

1、前缀和

表示:Si = a1 +a2 + a3 +...+ai下标一定从一开始

求Si : Si = Si-1 + ai

可以快速求出[l, r]一段数列的和

为了在边界可以统一公式,定义S0 = 0

一维前缀和

S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]

for(int i == 1; i <= n; i ++) s[i] = s[i - 1] + a[i];//前缀和的求解
printf("%d\n", s[r] - s[l]);

二维前缀和

/*
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
*/
for(int i = 1; i <= n; i++)
{    
    for(int j = 1; j <= m; j++)
    {
        scanf("%d", &a[i][j]);//读入a[i]
        s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
    }
}
printf("%\n", s[x2, y2] - s[x1 - 1, y2] - s[x2, y1 - 1] + s[x1 - 1][y1 - 1]);//求得子矩阵的和
        

2、差分

一维差分

首先给定一个原数组a:a[1], a[2], a[3],,,,,, a[n];

然后我们构造一个数组b : b[1], b[2], b[3],,,,,, b[i];

使得 a[i] = b[1] + b[2] + b[3] + ,,,,,, + b[i]

也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。

构造一个差分:

例如:

b1 = a1

b2 = a2 - a1

b3 = a3 - a2

但是在实际代码编写上只构造a, b的全为0的数组也可表示上述规则,这样我们就只需要考虑更新规则在构造是利用边界insert(i, i, a[])

//给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c

void insert(int l, int r, int c)
{
    b[l] += c;
    b[r + 1] -= c;
}

for(int i = 1; i <= n; i++) scanf("%d", &a[i]);//输入需要差分的数列

for(int i = 1; i <= n; i++) insert(i, i, a[i]);//利用边界将b[i]构造出来

//当数列中的一段需要整体的加上一个数是
scanf("%d%d%d", &l, &r, &c);//输入需要整体加的起始地址,终止地址,和需要加的数
insert(l, r, c)

for(int i = 1; i <= n; i++) b[i] += b[i - 1];//因为a[i] = b[1] + ..b[i],每个b[i] 就都对应区间加了之后的数列了
for(int i = 1; i <= n; i++) prinf("%d", b[i]);//输出加完的数列

二维差分

a[][]数组是b[][]数组的前缀和数组,那么b[][]a[][]的差分数组

原数组: a[i][j]

我们去构造差分数组: b[i][j]

使得a数组中a[i][j]b数组左上角(1,1)到右下角(i,j)所包围矩形元素的和。

始终要记得,a数组是b数组的前缀和数组,比如对b数组的b[i][j]的修改,会影响到a数组中从a[i][j]及往后的每一个数。

假定我们已经构造好了b数组,类比一维差分,我们执行以下操作
来使被选中的子矩阵中的每个元素的值加上c

/*给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c*/

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++)
    {
        scanf("%d", &a[i][j]);
        insert(i, j, i, j, a[i][j]);//构造b[][]差分
    }
} 

insert(x1, y1, x2, y2, c)//将(x1, y1)(x2, y2)之间的数都加上c

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[i]都加上一个做正方形对应了加完c后的a[i]的数列
    }
} 

部分笔记学习于前缀和与差分 图文并茂 超详细整理(全网最通俗易懂)

以上学习自AcWing

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ˇasushiro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值