前缀和详解

一维前缀和

一维前缀和的定义

一维前缀和的原理很简单,就是将第 i i i 个元素及其之前的所有元素之和预先处理好,方便以后 O ( 1 ) O(1) O(1) 查找的一种预处理算法。

一维前缀和的应用

洛谷里有板子题:P8218
讲解都已经写在代码里了。。。

#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
#define INT __int128
#define tbl ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n,m,a[114514];
int main()
{
//	一维前缀和:z[i]=a[i]+a[i-1]+a[i-2]+…+a[2]+a[1]=z[i-1]+a[i] (用于预处理,降低时间复杂度)
  tbl;
  cin>>n;
  for(int i=1;i<=n;++i)
  	cin>>a[i],a[i]+=a[i-1];//输入a[i]并直接将其前缀和
	cin>>m;
	for(int i=1;i<=m;++i){
		int x,y;
		cin>>x>>y;//读入区间
		cout<<a[y]-a[x-1]<<endl;
//		a[y]表示的区间为 1 部分,a[x-1]表示的区间为 2 部分,相减可得部分 3
//		111111111100000
//		222220000000000
//		000001111100000
//		因为它是[x,y](左闭右闭 )区间,所以应该是a[y]-a[x-1]
//    试想:如果它是(x,y](左开有闭)或[x,y)(左闭右开)的区间,有应该是哪两数相减呢?
	}
  return 0;
}

其他也没什么可说的了。。。那就下一个吧。。。

二维前缀和

二维前缀和的定义

二维前缀和就有些难理解了,具体定义如下:
建立在一维前缀和之上,我们要求一个矩阵内一个任意的子矩阵的数的和,我们就可以用二维前缀和。二维前缀和的状态和一维前缀和差不多,只不过我们多加了一维, f ( i , j ) f(i,j) f(i,j) 表示 ( 1 , 1 ) (1,1) (1,1) 这个点与 ( i , j ) (i,j) (i,j) 这个点两个点分别为左上角和右下角所组成的矩阵内的数的和

我先把状态转移方程告诉大家: f ( i , j ) = f ( i − 1 , j ) + f ( j − 1 , i ) − f ( i − 1 , j − 1 ) + v a l i , j f(i,j)=f(i-1,j)+f(j-1,i)-f(i-1,j-1)+{val}_{i,j} f(i,j)=f(i1,j)+f(j1,i)f(i1,j1)+vali,j
v a l i , j {val}_{i,j} vali,j 表示点 ( i , j ) (i,j) (i,j) 的值

具体该如何实现呢?
我们可以手动模拟一下。。。
请添加图片描述
一开始是这样的: f ( i , j ) f(i,j) f(i,j) 0 0 0,即图中所有格子都是白色的。
在这里插入图片描述
我们第一个加的是 f ( i , j − 1 ) f(i,j-1) f(i,j1) 所以我们将 ( 1 , 1 ) (1,1) (1,1)~ ( i , j − 1 ) (i,j−1) (i,j1) 染成黄色,表示加过一次。如上图所示。请添加图片描述
此后再加上 f ( i − 1 , j ) f(i−1,j) f(i1,j),但是我们发现两次染色中有一部分是重复染色的。即: ( 1 , 1 ) (1,1) (1,1)~ ( i − 1 , j − 1 ) (i−1,j−1) (i1,j1),它们被染过两次色,在上图中我们将这一块区域染成棕色。
请添加图片描述
既然重复计算了,那我们就剪掉,即减去 f ( i − 1 , j − 1 ) f(i−1,j−1) f(i1,j1),此时 ( 1 , 1 ) (1,1) (1,1)~ ( i − 1 , j − 1 ) (i−1,j−1) (i1,j1) 又回归了黄色(即只染过一次色)。
请添加图片描述
我们发现:只需再把 ( i , j ) (i,j) (i,j) 这一格染成黄色(即再加上 v a l i , j {val}_{i,j} vali,j),便大功告成了!

二维前缀和的应用

二维前缀和在洛谷没有板子题,所以只能自己造一题了。(悲

题目:

有一个 n n n m m m 列的二维数组,并由 k k k 次提问,每次提问给出左上角的坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和右下角的坐标 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 求这个矩形内的所有元素之和。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ULL unsigned long long
#define INT __int128
#define tbl ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n,m,q,a[1140][5140];
int main() {
//	二维前缀和:z[i][j]=a[i][j]+a[i][j-1]+a[i][j-2]+…+a[i][2]+a[i][1]+
//											a[i-1][j]+a[i-1][j-1]+a[i-1][j-2]+…+a[i-1][2]+a[i-1][1]+
//																						……………
//											a[1][j]+a[1][j-1]+a[1][j-2]+…+a[1][2]+a[1][1]=
//											z[i-1][j]+z[i][j-1]-z[i-1][j-1]+a[i][j]
//											(用于预处理,降低时间复杂度)
//	稍微有点难理解:
//	z[i-1][j]的区间设为 1,z[i][j-1]的区间设为 2,z[i-1][j-1]的区间设为 3,
//  111111110000    222222200000   333333300000
//  111111110000    222222200000   333333300000
//	111111110000    222222200000   333333300000
//	000000000000    222222200000   000000000000
//  000000000000    000000000000   000000000000
//  将一二两图重合,不难发现,重合部分(即为多算的部分)正好为图三
//  所以: 	  图一   +    图二   -     图三    + 当前节点的值 = 当前节点的前缀和
//       	 z[i-1][j] + z[i][j-1] - z[i-1][j-1] +   a[i][j]    =      z[i][j]
	tbl;
	cin>>n>>m;
	for(int i=1; i<=n; ++i)
		for(int j=1; j<=m; ++j)
			cin>>a[i][j],a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];//输入a[i][j]并直接将其前缀和
	cin>>q;
	for(int i=1; i<=m; ++i) {
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x1>>y2;//读入区间
		cout<<a[x2][y2]-a[x1][y2]-a[x2][y1]+a[x1][y1]<<endl;
//	z[x1][y2]的区间设为 1,z[x2][y1]的区间设为 2,z[x1][]的区间设为 3,
//  111110000000    222222222000   333330000000
//  111110000000    222222222000   333330000000
//	111110000000    000000000000   000000000000
//	111110000000    000000000000   000000000000
//  000000000000    000000000000   000000000000
//  将一二两图重合,和之前一样,重合部分(即为多算的部分)正好为图三
//  所以:  大区域(即被减区间) -	   图一   -    图二   +    图三   
//       	     z[x2][y2]      - z[x1][y2] - z[x2][y1] + z[x1][y1] 
	}
	return 0;
}

点个赞吧。。。打了好久啊啊啊啊啊啊。。。谢谢各位大牛神犇!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最长公共前缀是指一组字符串中,所有字符串都有的最长的前缀。根据提供的代码,可以看出这是一个用C语言实现的求解最长公共前缀的函数。函数的输入是一个字符串数组和数组的大小,输出是一个字符指针,指向最长公共前缀的起始位置。 这个函数的实现思路是,首先取数组中第一个字符串的第一个字符作为初始比较字符,然后遍历数组中的每个字符串的相同位置的字符,如果有不相同的字符,则退出循环。如果所有字符串的相同位置的字符都相同,则将初始比较字符更新为下一个位置的字符,继续比较下一位置的字符,直到遍历完所有字符串或者找到不相同的字符。最后,将最长公共前缀的末尾字符置为'\0',返回最长公共前缀的起始位置。 这个函数的时间复杂度是O(n*m),其中n是字符串数组的大小,m是最长公共前缀的长度。 #### 引用[.reference_title] - *1* *2* [力扣:最长公共前缀(详解)](https://blog.csdn.net/weixin_73142957/article/details/129778838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [力扣专题——14. 最长公共前缀——最长公共前缀C语言解法](https://blog.csdn.net/qq_42479987/article/details/116953103)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值