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

前缀和与差分

一、前缀和(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
 }

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

相关推荐
Dfreedom.2 小时前
机器学习经典算法全景解析与演进脉络(无监督学习篇)
人工智能·学习·算法·机器学习·无监督学习
421!2 小时前
ESP32学习笔记之GPIO
开发语言·笔记·单片机·嵌入式硬件·学习·算法·fpga开发
夏日听雨眠2 小时前
数据结构(单循环链表)
数据结构·链表
智算菩萨2 小时前
【How Far Are We From AGI】4 AGI的“生理系统“——从算法架构到算力基座的工程革命
论文阅读·人工智能·深度学习·算法·ai·架构·agi
福赖2 小时前
《算法:生产车间》
算法
空空潍2 小时前
LeetCode力扣 hot100一刷完结
算法·leetcode
leaves falling2 小时前
搜索插入位置(第一个≥target的位置)
算法
历程里程碑2 小时前
41 .UDP -3 群聊功能实现:线程池助力多客户端通信
linux·开发语言·网络·数据结构·c++·网络协议·udp
lcreek2 小时前
LeetCode 1162.地图分析
算法·leetcode·bfs