前缀和

前缀和指数组或序列中每个位置之前的元素总和,可以把它理解为数学上的数列的前n项和,是一种重要的预处理方式,能大大降低查询的时间复杂度。

一维前缀和

原数组:a[1] ,a[2],a[3]…..a[n]

前缀和:sum[i]=a[1]+a[2]…..a[i]

求前缀和公式:sum[i]=sum[i-1]+a[i] ,s[0]=0;

序号i 0 1 2 3 4 5
原数组 a[6] 0 3 5 8 2 7
前缀和 sum[6] 0 3 8 16 18 25

作用:求原数组[l,r]的和sum

使用原数组计算,则需要从下标l到下标r遍历原数组求和:

sum=a[l]+a[l+1]…..+a[r];

使用前缀和计算:sum=sum[r]-sum[l-1]

一维前缀和案例

描述

输入n个整数,再输入m个区间,每个区间的起点下标L,终点下标R。对于每个区间,输出n个整数中从下标L到下标R的区间和

输入描述

第一行包括两个整数n和m。(1≤n,m≤100000)

第二行包括n个整数。(1≤整数≤100)接下来有m行,每行包含两个整数L和R,表示区间范围。(0<L≤R≤n)

输出描述

输出有m行,每行一个整数,表示一个区间和。

样例输入

10 3
2 1 3 6 4 20 15 10 4 11
3 7
1 9
5 8

样例输出

48
65
49
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m,l,r,a[N],sum[N];//sum[i]表示的状态就是从1到i的区间和 
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum[i]=a[i]+sum[i-1];//状态转移方程 
    }    
    cin>>m;
    for(int i=1;i<=m;i++){
        cin>>l>>r;
        cout<<sum[r]-sum[l-1]<<endl;
    }
    return 0;
}

差分

一维差分

差分相当于前缀和的逆运算

原数组: a[1],a[2]…..a[n]

差分数组:d[1],d[2]…..d[n]

d[n]=a[n]-a[n-1]

求前缀和公式:sum[i]=sum[i-1]+d[i] ;

序号 i 0 1 2 3 4 5
原数组 a[6] 0 3 5 8 2 7
差分数组 d[6] 0 3 2 3 -6 5

作用:求原数组[L,R]的数全部加上c ,即a[L]+c,a[L+1]+c, a[R]+c,可以通过操作差分数组进行计算: 即,d[L]+c,d[R+1]-c

(1)原数组a在[1,3]区间加10,得到新数组c:

序号 i 0 1 2 3 4 5
原数组 a[6] 0 3 5 8 2 7
新组 c[6] 0 13 15 18 2 7

(2)c[6]对应差分数组d c[i]=d[i]+c[i-1]

序号 i 0 1 2 3 4 5
新组 c[6] 0 13 15 18 2 7
差分数组 d[6] 0 13 2 3 -16 5

(3)a在[1,3]区间加10,对应的差分数组d的变化:d[1]+10,d[4]-10

因此:c[1]=d[1]+a[0] a[3]=d[3]+c[2]

解题过程:

(1)初始化原数组a

(2)求原数组a的差分数组d

(3)求原数组[L,R]的数全部加上c,可以直接操作差分数组d:d[L]+c,d[R+1]-c

(4)根据差分数组d ,求对应的前缀和数组。

一维差分案例

描述

在一个桌子上摆放了 n 个杯子,每个杯子中有一定量的水。小 A 同学负责向杯子中倒水,他总共倒了 k 次,每次会向从第 L 个杯子到第 R 个杯子中添加 P 毫升的水(注意:水只可能增加,不可能减少)。请问小 A 同学倒了 k 次水之后, n 个杯子每个杯子有多少毫升的水。

输入描述

第一行包含两个整数 n 和 k。

第二行包含 n 个整数,表示一开始每个杯子中水的毫升数。

接下来 k 行,每行包含三个整数 L,R,P表示一次操作。

输出描述

共一行,包含 n 个整数,表示最终 n 个杯子每个杯子有多少毫升的水。

样例输入

8 3
1 2 10 8 1 5 1 1
7 8 12
1 8 4
2 3 12

样例输出

5 18 26 12 5 9 17 17

代码实现

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,l,r,p,a[N],dif[N];
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++){
        cin>>a[i];//原数组 
        dif[i]=a[i]-a[i-1];//原数组的差分数组 
    }    
    for(int i=1;i<=k;i++){
        cin>>l>>r>>p;
        dif[l]+=p;//操作差分数组 
        dif[r+1]-=p;
    }
    //计算前缀和
    for(int i=1;i<=n;i++){
        a[i]=dif[i]+a[i-1];
        cout<<a[i]<<" ";
    } 
    return 0;
}

总结

  • 差分算法可以快速修改序列中某一段连续区间的值。过程如下:
  • 在原序列的基础上构造差分数组
  • 给区间[L , R] 的元素加上k,那么只需要将差分数组d [ L ] + = k ,d [ R + 1 ] − = k
  • 对差分数组计算前缀和来还原出更新之后的序列。
Copyright ©图灵之星 2024,转载需注明出处该文件修订时间: 2024-12-30 22:47:30

results matching ""

    No results matching ""