【模版】前缀和

问题引入:

【洛谷P8218】

## 题目描述

给定 n 个正整数组成的数列 a1,a2,,anm 个区间 [li,ri],分别求这 m 个区间的区间和。

对于所有测试数据,n,m105,ai104

 

最朴素的想法,就是对于每次询问,我们都用for循环进行 [li,ri]区间的求和,不难看出时间复杂度为O(n*m)。

而如果我们利用前缀和,便可以把时间复杂度优化成O(m+n)。

 

所以前缀和是一种预处理,用于降低查询时的时间复杂度。

 

一维前缀和的定义:

设si为数列a从第一个数到第i个数的和,当i>=1时,显然有s[i]=s[i-1]+a[i],这里s[i]数组记录的数值称为前缀和。

若要求出[li,ri]区间的和,只需要求s[r]-s[l-1],单次时间复杂度为O(1)

 

本题AC代码如下:

#include<iostream>
using namespace std;
#define MAXN 100010;
int n,m;
int a[MAXN],s[MAXN];

int main() {
    cin >> n;
    for (int i=1;i<=n;i++) {
        cin >> a[i];   //若要使用前缀和,数组最好从下标1开始
        s[i]=s[i-1]+a[i];
    }
    cin >> m;
    for (int i=1,l,r;i<=m;i++) {
        cin >> l >> r;
        cout << s[r]-s[l-1];
    }
    return 0
}

 

二维前缀和的定义:

我们讲一下什么是二维前缀和,建立在一维前缀和之上,我们要求一个矩阵内一个任意的子矩阵的数的和,我们就可以用二维前缀和。

设s[i,j]表示以(1,1)为矩阵左上角端点,(i,j)为左下角端点矩阵的和。首先计算s[i,j-1]和s[i-1,j]的和,红色区域被算了两遍,所以减去一个s[i-1,j-1]最后加上a[i,j]的值,就可以得到s[i,j]

递推式为:

s[i,j]=s[i-1,j]+s[i,j-1]-s[i-1,j-1]+a[i,j]

若需要快速求出以(x1,y1)为左上角端点,(x2,y2)为右下角端点的矩阵内元素的和

递推式为:

i=x1x2i=y1y2a[i,j]=s[x2,y2]-s[x2,y1-1]-s[x1-1,y2]+s[x1-1,y1-1]

实现代码:

#include<iostream>
#include<cstring>
using namespace std;
int dp[2000][2000],map[2000][2000];
int main()
{
	int m,n,k;//所给的矩阵是n*m的,有k组查询 
	cin >>n>>m>>k;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin >>map[i][j];
	memset(dp,0,sizeof(dp));
	for(int i=1;i<=n;i++)//预处理一波 
		for(int j=1;j<=m;j++)
			dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+map[i][j];
	for(int i=1;i<=k;i++)//接受查询 
	{
		int x1,x2,y1,y2;
		cin >>x1>>y1>>x2>>y2;
		cout <<(dp[x2][y2]+dp[x1-1][y1-1]-dp[x1-1][y2]-dp[x2][y1-1])<<endl;//O(1)查询 
	}
	return 0;
}

 

差分数组自己求前缀和

for (int i=1;i<=n;i++,)
        for (int j=1;j<=n;j++) 
            a[i][j] += a[i-1][j] + a[i][j-1] - a[i-1][j-1];

 

posted @   綾川雪絵  阅读(25)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示