前缀和,简单来说就是一个数组的前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;
}