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

前缀和与差分

一、前缀和(Prefix Sum)

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

1.概念

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

  • 定义:设原数组为a1..n,前缀和数组pre0..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.概念

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

  • 定义:设原数组为a1...n,差分数组d1...n+1满足:

    d1=a1,di=ai-ai-1 (i≥2)

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

便于更好理解我举个例子

arr 1 3 7 5 2 原数组

d 1 2 4 -2 -3 原数组的差分数组di=arri-arri-1 d1=arr1;

Pred 1 3 7 5 2 差分数组d的前缀和predi=predi-1+di

还有另一种方法,不计算差分,只计算差分增量。无论原始数组值是否为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 可能越界,需判断

    最终通过差分数组的前缀和还原操作后的数组:ai=d1+d2+...+di=predi

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

    答:d数组的前缀和是不断叠加的,d1++,那么pred1及其后面的数都会+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
 }

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

相关推荐
HjhIron16 小时前
面试常客:字符串算法从入门到进阶
算法·面试
吴佳浩18 小时前
DeepSeek DSpark:Confidence-Scheduled Speculative Decoding 技术解析
人工智能·算法·deepseek
触底反弹19 小时前
🧠 搞懂 Token,才算真正入门大模型——从分词原理到 Embedding 语义实战
javascript·人工智能·算法
vivo互联网技术1 天前
ICLR 2026 | 基于后验采样的图像恢复方法LearnIR:人脸去阴影、去雾
人工智能·算法·aigc
浮生望1 天前
JS字符串与回文算法:从包装类到双指针的面试进阶之路
javascript·算法
黄敬峰1 天前
面试必刷:从JS底层包装类到双指针,彻底搞懂字符串与回文算法
算法
地平线开发者1 天前
J6B vio scenario sample
算法
BothSavage2 天前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn2 天前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法