*********前缀和*********
1、什么是前缀和?
陶陶学了数组以后,他对数组之间的累加和特别感兴趣,于是他就提出了要求 数组 元素之间的累加和的问题。
例如:现在有长度为 10 的数组: 3 2 5 1 4 5 3 7 8 2
从第2个元素到 第 5个元素的和就是 2+5+1+4=12
从第4个元素到第9个元素的和就是 1+4+5+3+7+8=28
输入格式
第一行 n k 两个正整数,分别表示 数组有n个元素,k次求和查询 (0<=n,k<=100000)
第二行为n个整数 数组的n个元素
以下有k行,每行两个元素,为求和的起点和终点
输出格式
k行,每行为起点到终点的和.
input
10 2
3 2 5 1 4 5 3 7 8 2
2 5
4 9
output
12
28
题解:
对于给出的一段n个整数数列,给出k次查询,每次查询给出区间【L,R】,让求区间内的整数之和对于这道题,我首先想到的是最简单的,题目怎么说,我就怎么做,先整一个for循环读入n个整数,然后在每次查询所给出的L,R之间写一个for循环进行区间值的加和。但我忽略了时间复杂度,这个方法对于小数据的加和可行,但是对于大数据就会出现超时问题,该时间复杂度为O(n*n),那么就需要降低时间复杂度,我们可以把计算区间和的那一层for循环给省掉,我们可以在读入n个整数时进行加和处理,把前i个数的和相加后赋给数组第i个元素。这样,我们在每次查询求区间和时只需计算a[R]-a[L-1]即可。前缀和顾名思义就是前面i个数的总和。
以下是代码对比:
#include <iostream>
using namespace std;
int array[100005];
int main() {
int n, k, sum;
cin >> n >> k;
int i, start, end, j;
for (i = 1; i <= n; i++) {
cin >> array[i];
}
for (i = 1; i <= k; i++) {
cin >> start >> end;
sum = 0;
for (j = start; j <= end; j++) {
sum = sum + array[j];
}
cout << sum << endl;
}
return 0;
}
#include<iostream>
int nums[100002],sum;
using namespace std;
int main(){
int n,k;
cin>>n>>k;
int i,start,end;
for(i=1;i<=n;i++){
int t;
cin>>t;
sum=sum+t;
nums[i]=sum;
}
for(i=1;i<=k;i++){
cin>>start>>end;
cout<<nums[end]-nums[start-1]<<endl;
}
return 0;
}
给你一串长度为n的数列a1,a2,a3......an,要求对a[L]~a[R]进行m次操作:
操作一:将a[L]~a[R]内的元素都加上P
操作二:将a[L]~a[R]内的元素都减去P
最后再给出一个询问求a[L]-a[R]内的元素之和?
你会怎么做呢?你可能会想,我对于m次操作每次都遍历一遍a[L]~a[R],给区间里的数都加上P或减去P,最后再求一次前缀和就行了。没错,这样子确实也能得出正确答案,但时间复杂度却高达O(M*n+q),对于1<=n,m<=1e5这个数据范围来说直接就tle了,所以说这个方法不可行。既然这样不行的话,那我们要怎么做才能快速的得到正确答案呢?是的,这个时候我们的差分就该派上用场了,我们新开一个数组b,储存每一次的修改操作,最后求前缀和的时候统计一下就能快速的得到正确答案了,详细请看下面代码。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+9;
int a[maxn],b[maxn];
int main(){
int i,j,k,n,m,p;
cin>>n>>m;
for(i=1;i<=n;i++){
cin>>a[i];
}
for(i=1;i<=m;i++){
int L,R,t;
cin>>t>>L>>R>>p;
if(t==1){
b[L]+=p;b[R+1]-=p; //仔细想想为什么b[R+1]要减去p
}
else{
b[L]-=p;b[R+1]+=p;
}
}
int add=0;
for(i=1;i<=n;i++){
add+=b[i];
a[i]+=a[i-1]+add;
}
int x,y;
cin>>x>>y;
cout<<a[y]-a[x-1]<<endl;
}
相信看到这里,大家已经仔细思考过代码了,为什么操作一时b[R+1]要减去p,很简单,因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没必要加p,所以需要减掉p。
定义:
对于已知有n个元素的离散数列d,我们可以建立一个记录它的每项与前一项差值的差分数组f;可见,f[1]=d[1]-0;对于2<=i<=n;f[i]=d[i]-d[i-1];将原本对d数列的操作转移到对f数列的操作,最终通过合并f数列来求得对应加和后的的数列。d[i]=f[i]+f[i-1]+......+f[1];这样叫做差分法。
例子:
(1)现有一个序列:3 4 1 5 6 2 7 9
(2)第一步根据公式f[i]=d[i]-d[i-1]求f序列数组,初始化d[0]=0;所以差分数列f:3 1 -3 4 1 -4 5 2
(3)第二步需要对区间[1,6]的所有元素加上1(另d[1]=d[i]+1;d[7]=d[7]-1;),得f数列的值f:4 1 -3 4 1 -4 4 2
(4)根据公式d[i]=f[i]+f[i-1]+f[i-2]+....f[1];得f:4 5 2 6 7 3 7 9
题解:
我们只操作了两步,另d[L]=d[L]+p(想要加的值);d[R+1]=d[R+1]-p;只需要关注左右边界即可
至于为什么这么做呢?
原理: