当前位置: 首页 > news >正文

蓝桥杯之差分题型

一维差分

问题描述

给定一个长度为 nn 的序列 aa

再给定 mm 组操作,每次操作给定 33 个正整数 l,r,dl,r,d,表示对 al∼ralr​ 中的所有数增加 dd

最终输出操作结束后的序列 aa

Update:由于评测机过快,n,mn,m 于 2024-12-09 从 105105 加强至 2×1052×105,杜绝暴力通过本题。 

输入格式

第一行输入两个正整数 n,mn,m。(1≤n,m≤2×1051≤n,m≤2×105)

第二行输入 nn 个正整数 aiai​。(1≤i≤n,1≤ai≤1041≤in,1≤ai​≤104)。

接下来 mm 行,每行输入 33 个正整数 l,r,dl,r,d。(1≤l≤r≤n,−104≤d≤1041≤lrn,−104≤d≤104)。

输出格式

输出 nn 个整数,表示操作结束后的序列 aa

样例输入

6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

样例输出

3 4 5 3 4 2

思路解析

1. 输入数据
  • 读取数组的长度 n 和操作的次数 m。

  • 创建两个数组:

    • a:存储原始数组的值。

    • sum:差分数组,用于记录每个位置的变化。

2. 构建差分数组
  • 遍历数组 a,计算差分数组 sum

    sum[i]=a[i]−a[i−1]
    • 解释

      • 差分数组 sum 的每个位置存储了当前元素与前一个元素的差值。

      • 通过差分数组,可以高效地对区间进行加法操作。

3. 处理区间加法操作
  • 对于每次操作,指定区间 [l,r] 和值 tmp:

    • 在差分数组 sum 中:

      • sum[l] += \text{tmp}:表示从位置 l 开始的区间增加 tmp。

      • sum[r+1] -= \text{tmp}:表示在位置 r+1 之后的区间减去 tmp,以抵消前面的增加操作。

    • 注意:如果 r+1 超出数组范围,则不需要减去 tmp。

4. 恢复原始数组
  • 遍历差分数组 sum,恢复更新后的数组 a

    a[i]=a[i−1]+sum[i]
    • 解释

      • 通过累加差分数组的值,可以恢复每个位置的最终值。

5. 输出结果
  • 输出更新后的数组 a

 代码实现

import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scan = new Scanner(System.in);// 读取数组长度和操作次数int n = scan.nextInt();int m = scan.nextInt();// 创建数组存储原始数组和差分数组int[] a = new int[n + 1];int[] sum = new int[n + 1];// 输入数组并构建差分数组for (int i = 1; i <= n; i++) {a[i] = scan.nextInt();sum[i] = a[i] - a[i - 1];}// 处理区间加法操作for (int i = 1; i <= m; i++) {int l = scan.nextInt();int r = scan.nextInt();int tmp = scan.nextInt();// 对区间 [l, r] 进行更新sum[l] += tmp;if (r + 1 <= n) {sum[r + 1] -= tmp;}}// 恢复更新后的数组for (int i = 1; i <= n; i++) {a[i] = a[i - 1] + sum[i];System.out.print(a[i] + " ");}scan.close();}
}

总结

  1. 核心思路

    • 利用差分数组高效地处理区间加法操作。

    • 通过差分数组的累加恢复最终的数组。

  2. 关键步骤

    • 构建差分数组

      sum[i]=a[i]−a[i−1]
    • 处理区间加法

      sum[l]+=tmpsum[r+1]−=tmp(如果 r+1≤n)
    • 恢复数组

      a[i]=a[i−1]+sum[i]
  3. 优点

    • 时间复杂度:每次区间加法操作的时间复杂度为 O(1),恢复数组的时间复杂度为 O(n)。

    • 空间复杂度:额外空间复杂度为 O(n)。

二维差分

问题描述

给定一个 n×mn×m 大小的矩阵 AA。

给定 qq 组操作,每次操作为给定 55 个正整数 x1,y1,x2,y2,dx1​,y1​,x2​,y2​,d,Ax1,y1Ax1​,y1​​ 是子矩阵左上角端点,Ax2,y2Ax2​,y2​​ 是子矩阵右下角端点,你需要给其中每个元素都增加 dd。

输出操作结束后的矩阵 AA。

输入格式

第一行输入 33 个正整数 n,m,qn,m,q。(1≤n,m≤103,1≤q≤1051≤n,m≤103,1≤q≤105)

接下来 nn 行每行输入 mm 个整数,表示 Ai,jAi,j​。(−103≤Ai,j≤103,1≤i≤n,1≤j≤m)(−103≤Ai,j​≤103,1≤i≤n,1≤j≤m)

接下来 qq 行,每行输入 55 个正整数 x1,y1,x2,y2,dx1​,y1​,x2​,y2​,d。(1≤x1≤x2≤n,1≤y1≤y2≤m,−103≤d≤103)(1≤x1​≤x2​≤n,1≤y1​≤y2​≤m,−103≤d≤103)

输出格式

输出 nn 行 mm 个整数,表示操作结束后的矩阵 AA。

样例输入

3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1

样例输出

2 3 4 1
4 3 4 1
2 2 2 2

思路解析

1. 输入数据
  • 读取矩阵的行数 n 和列数 m,以及操作的次数 q。

  • 创建两个二维数组:

    • a:存储原始矩阵的值。

    • sum:差分数组,用于记录每个位置的变化。

2. 构建差分数组
  • 遍历矩阵 a,计算差分数组 sum

    sum[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]
    • 解释

      • 差分数组 sum 的每个位置存储了当前元素与相邻元素的差值。

      • 通过差分数组,可以高效地对区间进行加法操作。

3. 处理区间加法操作
  • 对于每次操作,指定区间 [x1,y1] 到 [x2,y2] 和值 tmp:

    • 在差分数组 sum 中:

      • sum[x1][y1] += \text{tmp}:表示从位置 [x1,y1] 开始的区间增加 tmp。

      • sum[x2+1][y1] -= \text{tmp}:表示在位置 [x2+1,y1] 之后的区间减去 tmp,以抵消前面的增加操作。

      • sum[x1][y2+1] -= \text{tmp}:表示在位置 [x1,y2+1] 之后的区间减去 tmp,以抵消前面的增加操作。

      • sum[x2+1][y2+1] += \text{tmp}:表示在位置 [x2+1,y2+1] 之后的区间加上 tmp,以抵消前面的减法操作。

    • 注意:如果某个位置超出矩阵范围,则不需要进行操作。

4. 恢复原始矩阵
  • 遍历差分数组 sum,恢复更新后的矩阵 a

    a[i][j]=a[i−1][j]+a[i][j−1]−a[i−1][j−1]+sum[i][j]
    • 解释

      • 通过累加差分数组的值,可以恢复每个位置的最终值。

5. 输出结果
  • 输出更新后的矩阵 a

代码实现

import java.util.*;public class Main {static final int N = 1000 + 10;static int[][] a = new int[N][N];static int[][] sum = new int[N][N];// 差分操作函数public static void f(int x1, int y1, int x2, int y2, int tmp) {sum[x1][y1] += tmp;sum[x2 + 1][y1] -= tmp;sum[x1][y2 + 1] -= tmp;sum[x2 + 1][y2 + 1] += tmp;}public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 读取矩阵的行数、列数和操作次数int n = sc.nextInt();int m = sc.nextInt();int q = sc.nextInt();// 输入矩阵并构建差分数组for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {a[i][j] = sc.nextInt();sum[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];}}// 处理区间加法操作while (q-- > 0) {int x1 = sc.nextInt();int y1 = sc.nextInt();int x2 = sc.nextInt();int y2 = sc.nextInt();int tmp = sc.nextInt();f(x1, y1, x2, y2, tmp);}// 恢复矩阵并输出结果for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + sum[i][j];System.out.print(a[i][j] + " ");}System.out.println();}sc.close();}
}

总结

  1. 核心思路

    • 利用二维差分数组高效地处理区间加法操作。

    • 通过差分数组的累加恢复最终的矩阵。

  2. 关键步骤

    • 构建差分数组

      sum[i][j]=a[i][j]−a[i−1][j]−a[i][j−1]+a[i−1][j−1]
    • 处理区间加法

      sum[x1][y1]+=tmpsum[x2+1][y1]−=tmpsum[x1][y2+1]−=tmpsum[x2+1][y2+1]+=tmp
    • 恢复矩阵

      a[i][j]=a[i−1][j]+a[i][j−1]−a[i−1][j−1]+sum[i][j]
  3. 优点

    • 时间复杂度:每次区间加法操作的时间复杂度为 O(1),恢复矩阵的时间复杂度为 O(n×m)。

    • 空间复杂度:额外空间复杂度为 O(n×m)。

相关的习题练习

棋盘
问题描述

小蓝拥有 n×nn×n 大小的棋盘,一开始棋盘上全都是白子。小蓝进行了 mm 次操作,每次操作会将棋盘上某个范围内的所有棋子的颜色取反(也就是白色棋子变为黑色,黑色棋子变为白色)。请输出所有操作做完后棋盘上每个棋子的颜色。

输入格式

输入的第一行包含两个整数 nn,mm,用一个空格分隔,表示棋盘大小与操作数。

接下来 mm 行每行包含四个整数 x1x1​,y1y1​,x2x2​,y2y2​,相邻整数之间使用一个空格分隔,表示将在 x1x1​ 至 x2x2​ 行和 y1y1​ 至 y2y2​ 列中的棋子颜色取反。

输出格式

输出 nn 行,每行 nn 个 00 或 11 表示该位置棋子的颜色。如果是白色则输出 00,否则输出 11。

样例输入

3 3
1 1 2 2
2 2 3 3
1 1 3 3

样例输出

001
010
100

评测用例规模与约定

对于 3030% 的评测用例,n,m≤500n,m≤500 ;

对于所有评测用例,1≤n,m≤20001≤n,m≤2000,1≤x1≤x2≤n1≤x1​≤x2​≤n,1≤y1≤y2≤m1≤y1​≤y2​≤m。

思路解析

1. 输入数据
  • 读取棋盘的大小 n 和操作的次数 m。

  • 创建两个二维数组:

    • b:二维差分数组,用于记录每次翻转操作的影响。

    • s:二维前缀和数组,用于计算每个位置的翻转次数。

2. 处理翻转操作
  • 对于每次操作,指定矩形区域 [x1,y1] 到 [x2,y2]:

    • 在差分数组 b 中:

      • b[x1][y1] += 1:表示从位置 [x1,y1] 开始的区域增加一次翻转。

      • b[x2 + 1][y1] -= 1:表示在位置 [x2+1,y1] 之后的区域减去一次翻转,以抵消前面的增加操作。

      • b[x1][y2 + 1] -= 1:表示在位置 [x1,y2+1] 之后的区域减去一次翻转,以抵消前面的增加操作。

      • b[x2 + 1][y2 + 1] += 1:表示在位置 [x2+1,y2+1] 之后的区域加上一次翻转,以抵消前面的减法操作。

    • 注意:如果某个位置超出棋盘范围,则不需要进行操作。

3. 计算二维前缀和
  • 遍历差分数组 b,计算每个位置的翻转次数:

    s[i][j]=b[i][j]+s[i−1][j]+s[i][j−1]−s[i−1][j−1]
    • 解释

      • 通过累加差分数组的值,可以计算每个位置的翻转次数。

4. 确定最终状态
  • 遍历二维前缀和数组 s,判断每个位置的翻转次数的奇偶性:

    • 如果翻转次数为奇数,表示该位置的颜色被翻转了奇数次,最终状态为白色(假设初始状态为黑色)。

    • 如果翻转次数为偶数,表示该位置的颜色被翻转了偶数次,最终状态为黑色。

    • 输出每个位置的最终状态。

代码实现

import java.util.Scanner;public class Main {static final int N = 2000 + 10; // 假设最大值为2000,加上一些缓冲static int[][] b = new int[N][N]; // 二维差分数组,用于记录翻转操作的影响static int[][] s = new int[N][N]; // 二维前缀和数组,用于计算每个位置的翻转次数static int n, m;public static void main(String[] args) {Scanner sc = new Scanner(System.in);// 读取输入n = sc.nextInt(); // 棋盘大小m = sc.nextInt(); // 操作次数// 处理每个翻转操作while (m-- > 0) {int x1 = sc.nextInt();int y1 = sc.nextInt();int x2 = sc.nextInt();int y2 = sc.nextInt();// 更新二维差分数组 bb[x1][y1] += 1;if (x2 + 1 < N) b[x2 + 1][y1] -= 1;if (y2 + 1 < N) b[x1][y2 + 1] -= 1;if (x2 + 1 < N && y2 + 1 < N) b[x2 + 1][y2 + 1] += 1;}// 计算二维前缀和并确定最终状态for (int i = 1; i <= n; i++) {for (int j = 1; j <= n; j++) {// 计算二维前缀和s[i][j] = b[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];// 判断翻转次数的奇偶性,确定最终颜色System.out.print(s[i][j] % 2);}System.out.println(); // 换行}sc.close(); // 关闭输入流}
}

总结

  1. 核心思路

    • 利用二维差分数组高效地处理区间翻转操作。

    • 通过二维前缀和数组计算每个位置的翻转次数,判断奇偶性确定最终状态。

  2. 关键步骤

    • 处理翻转操作

      b[x1][y1]+=1b[x2+1][y1]−=1b[x1][y2+1]−=1b[x2+1][y2+1]+=1
    • 计算二维前缀和

      s[i][j]=b[i][j]+s[i−1][j]+s[i][j−1]−s[i−1][j−1]
    • 确定最终状态

      最终状态=s[i][j]%2
  3. 优点

    • 时间复杂度:每次翻转操作的时间复杂度为 O(1),计算前缀和的时间复杂度为 O(n2)。

    • 空间复杂度:额外空间复杂度为 O(n2)。

  自学蓝桥杯笔记,希望我们可以一起学习!

相关文章:

  • 电脑开机启动慢的原因
  • 数据结构初阶:二叉树(二)
  • zemax非序列棱镜面元理解
  • 4.18---缓存相关问题(操作原子性,击穿,穿透,雪崩,redis优势)
  • 电子电器架构 --- EOL 工厂刷写(产线)
  • 通信算法之267 : DJI无人机 云哨 DroneID 640ms
  • 【嵌入式八股11】STM32
  • 【无人机】电子速度控制器 (ESC) 驱动电机,常见的电调协议,PWM协议,Oneshot协议,DShot协议
  • 第十二讲、Isaaclab中使用RL对智能体进行训练
  • Let C语言通俗化
  • 深度解析商标显著性
  • 进程间通信(IPC)----共享内存
  • STM32单片机入门学习——第40节: [11-5] 硬件SPI读写W25Q64
  • OpenVINO怎么用
  • 限制外网用户WEB管理设备
  • npm i 安装遇到问题
  • 使用veaury,在vue项目中运行react组件
  • android的配置检查 查看安卓设备配置
  • 力扣面试150题--两数之和 和 快乐数
  • 第七篇:系统分析师第三遍——1、2章
  • 全国首家由司法行政部门赋码登记的商事调解组织落户上海
  • 江苏银行去年净赚超318亿增超10%,不良贷款率持平
  • 不断深化“数字上海”建设!上海市数据发展管理工作领导小组会议举行
  • 湖南省委书记人民日报撰文:坚定不移贯彻总体国家安全观,更好统筹高质量发展和高水平安全
  • 国家主席习近平出席柬埔寨国王西哈莫尼举行的欢迎仪式
  • 伊朗外交部:美矛盾立场被视为缺乏谈判的严肃性和诚意