算法——差分

ops/2024/12/12 2:18:30/

差分可以看作是前缀和的逆运算,前缀和可以帮我们快速得到某个区间的和,而差分就是我们将原数组看作是一个前缀和数组(q[])我们去构造一个差分数组(c[])

一维差分

使存在如下关系:

q[i] = c[1] + c[2] + c[3] +.....+ c[i]

-> q[i] = q[i - 1] + c[i]

-> c[i] = q[i] - q[i - 1]

eg: q = {2, 3, 6, 1, 7},下标从1开始方便讲解

按照上述规律我们可以构造出c = {2, 1, 3, -5, 6}

那么差分到底有什么用呢?

假设有这么一个场景我们现在给定一个区间[l, r]现在我们需要对原数组的[l, r]的数据都加上5,最直接的想法就是我们从l开始遍历到r然后给每个数+5,时间复杂度是O(n),如果我们现在需要执行m次如上操作就是O(n*m)这个开销不算太好。这时我们的差分就堂堂登场了,还记得差分的定义吗,原数组就是差分数组的前缀和,如果我们令c[l] + 5就是令q[l....]+5(因为l后的元素都会+c[l]),令c[r + 1] - 5就是令q[r+1....] - 5(因为r+1后的元素都会+c[r+1]),这两个一叠加就变成了q[l...r]+5了,我们只需要O(1)就能完成修改,执行m次的开销也只花费O(m),这有了极大的提升对于原来的暴力法。

代码实现:

int n, m;//n个数,m次修改
int q[n + 1], c[n + 1];void change(int l, int r, int value) {c[l] += value;if(r + 1 <= n) c[r + 1] -= value;
}int main() {for(int i = 1; i <= n; i++) c[i] = q[i] - q[i - 1];for(int i = 1; i <= m; i++) {int l, r, value;cin >> l >> r >> value;change(l, r, value);}//得到经过m次修改后的数组for(int i = 1; i <= n; i++) {q[i] = q[i - 1] + c[i];}
}

二维差分

二维差分的作用与一维差分是一样的,都是用来快速修改某个范围的数据,不过是从[l, r],变成了左上顶点(x1, y1) -> 右下顶点(x2, y2)。

我们还是先来确定一下原数组和差分数组的关系,q[x] [y]等于c[1] [1]到c[x] [y](左上顶点和右下顶点框出的矩阵)内的数据和:

q[x] [y] = c[1] [1] + ... + c[1] [y] + c[2] [1] + ...+c[2] [y] + ... + c[x] [1] +...+ c[x] [y];

我们可以画图来展现一下c数组:

 

通过上述规律我们可以知道:

红色:q[x-1] [y-1]

蓝色:q[x] [y-1]

深蓝色:q[x-1] [y]

绿色:c[x] [y]

我们可以得到这样的推论:q[x] [y] = q[x - 1] [y] + q[x] [ y-1] + c[x] [y] - q[x - 1] [y - 1],

移项后:c[x] [y] = q[x] [y] + q[x - 1] [y - 1] - q[x - 1] [y] - q[x] [y -1]

这样我们就可以通过原数组q来构造差分数组c

如果我们希望(x, y) 到 (x2, y2)范围内的数据都改变value。

 

参照一维差分的规律我们可以想到就是c[x] [y] +value,会使橙色范围的q+value,我们希望紫色范围的不发生改变,所以c[x2+1] [y] - value、c[x] [y2 + 1] - value, 但这样导致粉色区域多减了一次还需要加回来c[x2+1] [y2+1] + value,这样就完成了修改

代码实现:

int q[n+1][m+1], c[n+1][m+1];//下标都还从1开始void change(int x1, int y1, int x2, int y2, int value) {c[x1][y1] += value;//注意边界判断这里就不写了c[x2 + 1][y1] -= value;c[x1][y2 + 1] -= value;c[x2 + 1][y2 + 1] += value;
}int main() {//num次修改for(int i = 1; i <= num; i++) {int x1, y1, x2, y2, value;cin >> x1 >> y1 >> x2 >> y2 >> value;change(x1, y1, x2, y2, value);}//得到修改后的数据for(int i = 1; i <= n; i++) for(int j = 1; j <= m; j++) q[i][j] = q[i - 1][j] + q[i][j - 1] + c[i][j] - q[i - 1][j - 1];
}


http://www.ppmy.cn/ops/141136.html

相关文章

gitlab

一、gitlab 概念 GitLab 是一个用于仓库管理系统的开源项目&#xff0c;使用Git作为代码管理工具&#xff0c;并在此基础上搭建起来的web服务。 b和GitHub一样属于第三方基于Git开发的作品&#xff0c;免费且开源&#xff08;基于MIT协议&#xff09;&#xff0c;与Github类似…

ansible playbook 使用 script 模块在远程主机上执行脚本

需求一&#xff1a; 要在所有的远程主机上执行一下收集脚本&#xff0c;并在控制台打印出来 脚本如下&#xff1a; [root192 Playbooks]# cat CollectServerInformation.sh #!/bin/bash # Collect server information echo "Collecting server information..."# OS …

Scala 隐式转换

object test {//复习隐式转换&#xff1a;//隐式转换&#xff1a;编译器 偷偷地&#xff0c;自动地帮我们把一种数据转换为另一种类型//例如&#xff1a;int --> double//它有失败的时候&#xff08;double --> int&#xff09;&#xff0c;有成功的时候//当它转换失败的…

css 实现在一条线上流动小物体(offset-path)

直接贴代码&#xff0c;留几个参考网址给大家 【SVG】路径&#xff1c;Path&#xff1e;标签详解&#xff0c;一次搞懂所有命令参数 探秘神奇的运动路径动画 Motion Path <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8&quo…

LeetCode - #153 寻找旋转排序数组中的最小值

文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅&#xff08;Netflix 增长黑客&#xff0c;《iOS 面试之道》作者&#xff0c;ACE 职业健身教练。&#xff09;的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新…

Scala正则表达式全面指南:从基础到高级应用

引言 正则表达式是处理字符串的强有力工具&#xff0c;它允许开发者定义复杂的搜索模式&#xff0c;用于文本的搜索、匹配、替换和提取等。Scala语言通过scala.util.matching.Regex类提供了对正则表达式的支持&#xff0c;使得在Scala中进行文本处理变得高效而灵活。本文将全面…

【开源免费】基于SpringBoot+Vue.JS大创管理系统(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 081 &#xff0c;文末自助获取源码 \color{red}{T081&#xff0c;文末自助获取源码} T081&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

Prim 算法在不同权重范围内的性能分析及其实现

Prim 算法在不同权重范围内的性能分析及其实现 1. 边权重取值在 1 到 |V| 范围内伪代码C 代码实现2. 边权重取值在 1 到常数 W 之间结论Prim 算法是一种用于求解加权无向图的最小生成树(MST)的经典算法。它通过贪心策略逐步扩展生成树,确保每次选择的边都是当前生成树到未加…