【C++】一维、二维前缀和+例题+模板

引语

来学前缀和!

PS:此文中含有一个角色小A,我将会努力插入这个名为"小A"的角色来丰富文章趣味

正文

前缀和的定义

小A:前缀和?是什么?好吃吗,阿巴阿巴

前缀和,就是指某个序列的前n项的和

而前缀和算法,就是指通过求出某个序列的前n项的和来进行快速地区间和计算

前缀和还可以用来求一个二维矩阵的元素和

                                                                                                             ——本文章

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,而差分可以看成前缀和的逆运算。合理的使用前缀和与差分,可以将某些复杂的问题简单化。

                                                                                                             ——CSDN某文章1

假定给我们一个数组,前缀和就是该元素前所以元素和。也就是如果我们设定一个数组s为前缀和数组,那么s[3]就是我们原数组前三个元素之和,这就是我们的前缀和。

                                                                                                             ——CSDN某文章2

前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和。合理的使用前缀和预处理,能大大降低时间复杂度,构造前缀和数组需要O(n)时间复杂度和空间复杂度,查询操作只需要常数时间。

                                                                                                             ——CSDN某文章3

前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。分为一维和二维,一维前缀和可以很快速的求序列中某一段的和。而二维前缀和可以快速求一个矩阵中某个子矩阵的和。

作用:快速求出元素组中某段区间的和

                                                                                                             ——CSDN某文章4​​​​​​​​​​​​

定义:前缀和是指某一序列的前 n 项和。 

                                                                                                             ——CSDN某文章5

前缀和可以简单理解为「数列的前n项的和」,是一种重要的预处理方式,能大大降低查询的时间复杂度。​​​​​​​

                                                                                                             ——OI WiKiCSDN某文章5

小A:好,然后呢?

一维前缀和

例题引入

AcWing 795

思路1

小A:啊多简单啊,直接暴力!暴力YYDS!

旁白:"代码大牛"小A立马写好了一份代码

 

#include <bits/stdc++.h>
using namespace std;
long long n,m,a[100005];
int main(){
	cin >> n >> m;
	for (int i = 1;i <= n;i++) cin >> a[i];
	while (m--){
		long long l,r,sum = 0;
		cin >> l >> r;
		for (int i = l;i <= r;i++) sum += a[i];
		cout << sum << endl;
	}
	return 0;
}

旁白:果不其然,小A成功TLE

小A:啊?为什么 

你仔细看看数据范围

小A:1≤n,m≤.....100000?

旁白:恭喜玩家[小A]获得成就[不看数据范围的后果]

小A:那应该怎么做呢...

思路2 
预处理!
从第一个数字到某个数字为止,这样的区间总共有n 种(设n为元素总数)
​​​​​​​
从某个数字到n为止,这样的区间总共有n 种(设n为元素总数)

小A:所以我们要预处理什么?

当然是预处理这些区间的和啦!

观察下图

那么我们还可写成什么形式?

此处我们设l为2,r为4

所以说,区间[l,r]的和=数字总和-[1,l-1]的和-[r+1,n]的和

小A:哦!你的意思是让我们计算前面的和与后面的和?

其实也没有那么复杂啦

我们在以上改进,不计算后面的和,仅计算前面的和,我们使用数组sum[n]来预处理前n个数的和

小A:我知道了!sum[n]=a[1]+a[2]+...+a[n]!

正确!那么请你思考,如何根据sum数组求区间和呢?

小A:额...我知道了!

旁白:小A的图片:

小A:sum[r]就是sum[l-1]+区间[l,r],所以,区间[l,r]=sum[r]-sum[l-1]

旁白:小A立马开始着手写代码

小A:AC了!

 

总结模板

#include <bits/stdc++.h>
using namespace std;
long long n,m,a[100005],sum[100005];
int main(){
	cin >> n >> m;
	for (int i = 1;i <= n;i++) cin >> a[i],sum[i] = sum[i - 1] + a[i];
	while (m--){
		long long l,r;
		cin >> l >> r;
		cout << sum[r] - sum[l - 1] << endl;
	}
	return 0;
}

二维前缀和

例题引入

AcWing 796

思路1

小A:这次我可不会被坑了

旁白:剧情需要,小A被迫又在写暴力代码

#include <bits/stdc++.h>
using namespace std;
long long n,m,q,a[1005][1005];
int main(){
	cin >> n >> m >> q;
	for (int i = 1;i <= n;i++){
	    for (int j = 1;j <= m;j++) cin >> a[i][j];
	}
	while (q--){
		long long x1,y1,x2,y2,sum = 0;
		cin >> x1 >> y1 >> x2 >> y2;
		for (int i = x1;i <= x2;i++){
		    for (int j = y1;j <= y2;j++) sum += a[i][j];
		}
		cout << sum << endl;
	}
	return 0;
}

旁白:果不其然,小A又成功TLE  

旁白:恭喜玩家[小A]获得成就[被迫暴力TLE]

思路2

小A:我知道!肯定是预处理!

怎么预处理?

小A:额

上图片

我们可以将sum[i][j]表示为以i,j为右下角的矩阵元素和

我们发现,矩阵和是:S = 橙框区域+黄框区域-红框区域+绿框区域(有重叠) ,替换成sum值和矩阵a中的元素就是:   

sum[i][j] = sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1] + a[i][j]

小A:预处理我写好啦!

for (int i = 1;i <= n;i++){
	for (int j = 1;j <= m;j++){
		cin >> a[i][j];
		sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
	}
}

那么怎么用sum数组呢?

小A:容我思索一下 

我知道了!

小A:D = S(总和) - B - C + A 

小A:如果想要求出以(a,b)为左上角,(c,d)为右下角的子矩阵和

小A:就用:sum[c][d] - sum[a-1][b] - sum[a][b-1] + sum[a-1][b-1]

旁白:小A写出了代码

总结模板

#include <bits/stdc++.h>
using namespace std;
int n,m,q,a[1005][1005],sum[1005][1005];
int main(){
    cin >> n >> m >> q;
    for (int i = 1;i <= n;i++){
    	for (int j = 1;j <= m;j++){
    		cin >> a[i][j];
    		sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + a[i][j];
    	}
    }
    while (q--){
        int x1,y1,x2,y2;
        cin >> x1 >> y1 >> x2 >> y2;
        cout << sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1] << endl;
    }
    return 0;
}

结语

多多练习!

参考

1 2 3 4​​​​​​​ ​​​​​​​​​​​​​​5 6 勰码教育

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值