前缀和与差分(蓝桥杯高频考点)

前缀和与差分

一、前缀和(Prefix Sum)

引入:对于一个有n个元素的数组arr,进行q次询问[L,R]的区间和。如果q较小,我们可以直接for循环一个一个元素加起来,但如果q很大,R-L也很大,那么时间复杂度就会很大。因此我们可以用前缀和进行预处理,优化时间(我以下代码里的数组均是从1开始存数据而不是0)

1.概念

用于快速计算数组中任意区间[l,r]的元素和

  • 定义:设原数组为a[1..n],前缀和数组pre[0..n]满足:

    pre[i]=a[1]+a[2]+...a[i];类似于高中数学里的前n项和

其中pre[0]=0(哨兵值,便于计算)

  • 区间和公式:

    sum(l, r) = pre[r] - pre[l - 1];

2.用途

  • 多次查询区间和(如求第l到第r项的和)

  • 时间复杂度:预处理O(n),单次查询O(1)

3.C代码模板

cpp 复制代码
 #include <stdio.h>
 #define MAXN 100010
 ​
 int a[MAXN], pre[MAXN];
 ​
 int main() {
     int n, m;
     scanf("%d", &n);
     pre[0]=0;
     for (int i = 1; i <= n; i++) {
         scanf("%d", &a[i]);
         pre[i] = pre[i - 1] + a[i];  // 构建前缀和
     }
 ​
     scanf("%d", &m);
     while (m--) {
         int l, r;
         scanf("%d %d", &l, &r);
         printf("%d\n", pre[r] - pre[l - 1]);  // 区间和
     }
     return 0;
 }

二、差分

引入:对于一个有n个元素的数组,对它进行m次操作,每个操作给定一个L和,然后对数组区间[L,R]下标范围内的每个元素加上一个数x,最后输出m次操作后的数组arr(n个数m次操作1次询问)每次对区间数组进行修改的操作都用了O(n)的复杂度,一共是O(m*n),为了优化时间,可以用差分

1.概念

差分是前缀和的逆运算,用于高效实现区间加法操作。

  • 定义:设原数组为a[1...n],差分数组d[1...n+1]满足:

    d[1]=a[1],d[i]=a[i]-a[i-1] (i≥2)

  • 性质:原数组的差分数组d可通过其前缀和得到原数组

便于更好理解我举个例子

arr 1 3 7 5 2 原数组

d 1 2 4 -2 -3 原数组的差分数组d[i]=arr[i]-arr[i-1] d[1]=arr[1];

Pred 1 3 7 5 2 差分数组d的前缀和pred[i]=pred[i-1]+d[i]

还有另一种方法,不计算差分,只计算差分增量。无论原始数组值是否为0,我们都将差分数组初始化为0,d仅表示差分增量,计算d的前缀和后再加上原始数组可得到最终结果

例如:

初始arr 1 3 7 5 2 对第1~3元素+2变为 3 5 9 5 2

初始d 0 0 0 0 0

差分增量d 2 0 0 -2 0

差分增量d的前缀和 2 2 2 0 0

其前缀和加上初始arr 3 5 9 5 2 与操作后的数组arr一致

  • 核心操作:对区间[l,r]所有元素加相当于:

    复制代码
     d[l] += x;
     d[r + 1] -= x;   // 注意 r+1 可能越界,需判断

    最终通过差分数组的前缀和还原操作后的数组:a[i]=d[1]+d[2]+...+d[i]=pred[i]

    为什么区间[l,r]那么多个元素,却只需要对d[l]和d[r+1]操作就能得到结果?

    答:d数组的前缀和是不断叠加的,d[1]++,那么pred[1]及其后面的数都会+1

2.用途

  • 多次对区间进行加/减操作,最后输出整个数组

  • 时间复杂度:单次修改 O(1),还原 O(n)

3.C代码模板

法一

cpp 复制代码
 #include <stdio.h>
 #define MAXN 100010
 ​
 int d[MAXN];  // 差分数组,初始为0
 ​
 int main() {
     int n, m;
     scanf("%d %d", &n, &m);
 ​
     while (m--) {
         int l, r, x;
         scanf("%d %d %d", &l, &r, &x);
         d[l] += x;
         if (r + 1 <= n) d[r + 1] -= x;  // 防止越界
     }
 ​
     // 通过前缀和还原最终数组
     for (int i = 1; i <= n; i++) {
         d[i] += d[i - 1];  // 此时 d[i] 即为 a[i]
         printf("%d ", d[i]);
     }
     return 0;
 }

法二

cpp 复制代码
 #include <stdio.h>
 #define MAXN 100010
 ​
int main(){
     int n,m;
     int arr[MAXN];
     scanf("%d %d",&n,&m);
     int d[MAXN]={0};
     for(int i=1;i<=n;i++){
         scanf("%d",&arr[i]);
     }
     
     while(m--){
         int l,r,x;
         scanf("%d %d %d",&l,&r,&x);
         d[l]+=x;
         if(r+1<=n){
             d[r+1]-=x;   //此时d[i]为差分增量,而不是差分
         }
     }
     
     for(int i=1;i<=n;i++){
         d[i]+=d[i-1];    
         printf("%d ",d[i]+arr[i]);
     }
     return 0
 }

学习完以上内容后可以自行去搜索近五年蓝桥杯相关真题进行练习

相关推荐
计算机安禾7 小时前
【数据结构与算法】第35篇:归并排序与基数排序
c语言·数据结构·vscode·算法·排序算法·哈希算法·visual studio
专注API从业者7 小时前
淘宝商品详情 API 与爬虫技术的边界:合法接入与反爬策略的技术博弈
大数据·数据结构·数据库·爬虫
爱码小白7 小时前
MySQL 单表查询练习题汇总
数据库·python·算法
橘颂TA8 小时前
【笔试】算法的暴力美学——牛客 NC213140 :除2!
c++·算法·结构与算法
yoyobravery8 小时前
蓝桥杯第15届单片机满分
单片机·职场和发展·蓝桥杯
汀、人工智能8 小时前
[特殊字符] 第66课:跳跃游戏
数据结构·算法·数据库架构·图论·bfs·跳跃游戏
汀、人工智能8 小时前
[特殊字符] 第70课:加油站
数据结构·算法·数据库架构·图论·bfs·加油站
wsoz8 小时前
Leetcode普通数组-day5、6
c++·算法·leetcode·数组
y = xⁿ8 小时前
【LeetCode】双指针:同向快慢针
算法·leetcode
啊哦呃咦唔鱼8 小时前
LeetCode hot100-105从前序与中序遍历序列构造二叉树
算法