前缀和基础用法

前缀和,简单来说就是一个数组的前n项之和,是一个记录了一个数组的每一项的前n项之和的数组,举例来说,一个数组a{9,1,1,2,3,1,0,7},他的前缀和prefix就是{9,10,11,13,16,17,17,24},要从原数组得到前缀和数组,很明显只需要把preix[i]赋值为prefix[i-1]+a[i]即可.

接下来我来简单介绍前缀和的使用,对于一个数组a[n](下标从1到n),我要得到其中a[x]+a[x+1]+...a[y]的值(1<=x<=y<=n),可以使用的方法有两种

第一种,直接用循环遍历并加到总和total中

for(int i=x;i<=y;i++)
total+=a[i];

很容易看出这种方法的循环次数是y-x+1,也就是O(n)的时间复杂度,当查询次数很少时(比如1次或者2次)使用这种方法十分简便迅速,但是当查询次数很多,比如上1e6次,并且n的取值范围很大时,循环次数会变得非常多,此时我们就可以引入第二种方法

第二种,使用前缀和直接得到结果

首先我们对原数组求前缀和

int prefix[10000]={0}
for(int i=1;i<=n;i++) 
prefix[i]=prefix[i-1]+a[i];
//需要注意的是,prefix的下标和a的下标最好从1开始,如果从0开始,赋值prefix[0]时会越界导致段错误

我们依然使用文章开头的例子a{9,1,1,2,3,1,0,7},prefix{9,10,11,13,16,17,17,24}

根据前缀和的定义,prefix[i]等于a[i]+a[i-1]+a[i-2]...+a[1],因此,当我们想求原数组x到y的总和时,就可以使用a[1]+a[2]+a[3]+...a[x-1]+a[x]+...a[y]减去a[1]+a[2]+a[3]+...a[x-1]来得到,也就是用y位置的前缀和减去x-1位置的前缀和

例如我们想求a[3]到a[5]的和,其实就是a[1]到a[5]的和减去a[1]到a[2]的和,也就是preix[5]-prefix[2]=16-10=6.验证发现a[3]+a[4]+a[5]=1+2+3=6

使用这种方法,对于一个数组,我们只需要先处理一次O(n)的运算求出前缀和,之后每一次想得到一个区间和的时候,只需要用前缀和相减的方法进行一次运算就能得出结果

此处贴出求a[x]到a[y]的总和的代码

while(t--)//多组输入
{
int x,y;
cin>>x>>y;
cout<<prefix[y]-prefix[x-1];
}

 接下来让我们来利用前缀和完成一些例题

前缀和模版

题目描述

给定一个长度为n的数组a1,a2,.....an​.

接下来有q次查询, 每次查询有两个参数l, r.

对于每个询问, 请输出al+a(l+1)+....+ar​

输入描述:

第一行包含两个整数n和q.

第二行包含n个整数, 表示a1,a2,....an.

接下来q行,每行包含两个整数 l 和 r.

1≤ n,q ≤1e5
−1e9 ≤ a[i] ≤1e9
1≤ l ≤ r ≤n

输出描述:

输出q行,每行代表一次查询的结果.

题解:

这题就是一道非常经典的前缀和模板题,q为查询次数,最大为1e5,因此肯定不能使用直接累加的方法,我们可以利用前缀和完成

#include"bits/stdc++.h"
using namespace std;
int main()
{
int n,q,a[100007];
long long prefix[100007]={0};  //使用long long防止数据溢出 
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i];          //输入数组 
for(int i=1;i<=n;i++)
prefix[i]=prefix[i-1]+a[i];   //建立前缀和数组 
while(q--)
{
	int l,r;
	cin>>l>>r;
	cout<<prefix[r]-prefix[l-1]<<endl;
}
return 0;
}

如果要精简代码,也可以把输入数组和赋值前缀和数组放在同一个循环中完成

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值