差分及前缀和基础和例题总结

差分、前缀和有着特殊的关系,也是一种入门算法

首先考虑这样一个问题:
有 N 个的正整数放到数组 A 里,现在要求一个新的数组 B,新数组的第 i 个数 B[i]是原数组 A 第 0 到第 i 个数的和。

前缀和

前缀和想必大家都知道,给定一数组c[i],用另外一数组sum[n]来存储c[1]-c[n]的和,这样在求任意区间的和的时候,就非常的快捷了

具体递推为

sum[i] = c[i-1]+c[i]

差分

什么是差分?

差分是两个元素的差值,我们用一个新的数组a[i]记录差分的值,数组 a 叫做 差分数组

具体递推为

a[i] = c[i]-c[i-1]

差分和前缀和有什么关系呢

前缀和数组sum[]
差分数组a[]

差分数组a[]的前缀和是原数组
前缀和sum[]的差分数组是原数组

在实际的应用当中,对一个数组进行操作时,进行区间增加或者减少同样的数值,就可以直接在差分数组上进行修改,下次在查询某个点的值就可以通过差分数组的来求,十分的快捷

二维前缀和

现在有这样一个问题

给定一个n*m大小的矩阵a,有q次询问,每次询问给定x1,y1,x2,y2四个数,求以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素和,包括左上角和右下角的元素。

像这样的题目,我们首先想到的就是遍历求和,但是如果时多组输入的话,每次都一遍一遍的计算,很容易TLE,所以我们可以在原数组的基础上构造二维前缀和数组,求解的话就可以比较快了

如何构造二维前缀和数组?

二维数组的每个点等于其左边的点+上边的点-左上方的点,如果存在就计算,不存在则不计算,比如在边界上~

在这里插入图片描述
在这个图里,a = b+c-d + a
因为 b 和 c 中都加了一遍d,所以根据容斥原理,把多加的减掉
在这里插入图片描述

假如我想求a[2][4]的前缀和,我得先加上a[1][4]的前缀和,再加上a[2][3]的前缀和,然后这个时候我们发现实际上a[1][3]这个部分我们加了两遍,所以我们需要再减去一遍a[1][3],于是得出公式a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1]。

完整代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+9;
int a[maxn][maxn];
int main(){
	int i,j,k,n,m,q;
	cin>>n>>m>>q;
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++)
		cin>>a[i][j];
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=m;j++)
		a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
	}
	for(i=1;i<=q;i++){
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		int ans=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
		cout<<ans<<endl;
	}
}

例题
最高的牛

题意很简单,我们可以知道一点,在给出的相互能看见的牛对形成的区间是不会交叉的,因为每对牛中间的牛都比他们低

思路:如果两头牛看得见则中间的牛h-1,同时使用差分记录,最后再前缀和还原数组。

#include <iostream>
using namespace std;
const int N = 1e4+10;
int s[N];  //记录差分
bool flag[N][N];  //此处用int会超内存  int是4 个字节32位,bool只有1位
int main()
{
    int m, n, p, h;
    cin >> n >> p >> h >> m;
    int a, b;
    s[1] = h;
    for (int i = 1; i <= m; i++)
    {
        cin >> a >> b;
        if (a > b)
            swap(a, b);
        if (!flag[a][b])
        {
            s[a + 1]--;
            s[b]++;
            flag[a][b] = 1;
        }
    }
    for (int i = 1; i <= n; i++)
    {
        s[i] = s[i - 1] + s[i];
        cout << s[i] << endl;
    }
    return 0;
}

洛谷前缀和例题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值