Acwing 前缀与差分

news2024/10/23 23:29:15

1.一维前缀和

一维前缀和:S[i]=a1+a2+a3+a4+…+ai,要求a从a1开始,且S[0]=0

前缀和的作用:给定一组序列数据,可以计算任意第l个数到第r个数的和,S[r]-S[l-1](这里就解释了为什么要求S[0]=0,因为当l=1时,实质就是求是S[r])
求很多个任意子序列的数据和时,假如不使用前缀和公式,就需要顺序遍历来求,时间复杂度为O(n)
使用前缀和,直接得到结果,时间复杂度为O(1)

Acwing 795.前缀和
在这里插入图片描述
具体实现代码(详解版):

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;
int a[N],s[N];
int n,m;

int main(){
    cin >> n >> m;
    
    for(int i = 1 ; i <= n ; i ++) cin >> a[i];
    for(int i = 1 ; i <= n ; i ++) s[i] = s[i - 1] + a[i];//计算前缀和,s[0] = 0
    
    //处理询问
    while(m --){
        int l , r;
        cin >> l >> r;
        
        cout << s[r] - s[l - 1] << endl;//使用前缀和
    }
    
    return 0;
}

2.二维前缀和

在这里插入图片描述
二维前缀和:S[i][j]即为坐标(i,j)左上部分所有元素的和,也就是绿色区域的所有元素和(注意i,j依旧是从1开始,s[0][0]默认为0)
在这里插入图片描述
二维前缀和计算公式:s[i][j]=s[i][j-1]+s[i-1][j]-s[i-1][j-1]+a[i][j](注意这里要s[i][j-1]+s[i-1][j]多算了一次s[i-1][j-1],所以要减去一次)
在这里插入图片描述
任意子矩阵中所有数的和计算:

设所求子矩阵的左上角坐标为(x1,y1),右下角坐标为(x2,y2)

s=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1]

eg:s=S[5][3] - S[2][3] - S[5][1] + S[2][1]

在这里插入图片描述
具体实现代码(详解版):

#include <iostream>

using namespace std;

const int N = 1010; 

int a[N][N], s[N][N]; // a 用于存储原始矩阵,s 用于存储二维前缀和
int n, m, q; // n: 行数, m: 列数, q: 查询的次数

int main() {
   
    cin >> n >> m >> q;
    
   
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    
    // 计算前缀和矩阵 s
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            // 前缀和的计算公式
            s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
        }
    }
    
    // 处理 q 次查询
    while (q--) {
        int res = 0; // 存储子矩阵的和
        int x1, y1, x2, y2; // 子矩阵左上角 (x1, y1) 和右下角 (x2, y2)
        
        cin >> x1 >> y1 >> x2 >> y2;
        
        // 利用前缀和公式计算子矩阵的和
        res = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1];
        
        cout << res << endl;
    }
    
    return 0;
}

3.一维差分

差分的实质是前缀和的逆运算
假设有一个数组,a{1},a{2},a{3},a{4},a{5},…,a{n}
针对这个数组,构造出另一个数组,b{1},b{2},b{3},b{4},b{5},…,b{n}
使得a数组是b数组的前缀和,即使得 a{i} = b{1} + b{2} + … + b{i};b{i}=a{i}-a{i-1}

差分的作用:
若要对a数组中[l, r]区间内的全部元素都加上一个常数C,若直接操作a数组的话,需要循环遍历,时间复杂度是O(n)。而如果操作其差分数组b,则时间复杂度是O(1)。即可以用O(1)的时间给某一数组的某一子序列元素都加上一个值
具体实现:
数组a是数组b的前缀和数组,只要对 b{l}这个元素加C,则a数组从l位置之后的全部数都会被加上C,但r位置之后的所有数也都加了C,所以我们通过对 b{r+1} 这个数减去C,来保持a数组中r位置以后的数的值不变,即a数组r位置以后的数都是+C-C抵消。
于是,对a数组的[l, r]区间内的所有数都加上一个常数C,就可以转变为对 b{l}+C,对b{r+1}-C
对于构造差分数组b:
在输入数组a时,可以先假想数组a和数组b的全部元素都是0。然后每次进行一次插入操作(指的是对数组a的[l, r]区间的每个数加上常数C),比如对a数组区间[1,1],加(插入)常数a{1},效果就是 b{1}=b{1}+a{1}b{2}=b{2}-a{1};对区间[2,2],加常数a{2},效果就是 b{2}=b{2}+a{2}b{3}=b{3}-a{2},…,这样在输入数组a的同时,就能够快速构造出其差分数组b。实际就把构造数组b的过程看作为给一个数组中[l, r]区间内的全部元素都加上一个常数C的过程,只不过特殊的是这个数组元素都为0,这个区间为[i,i],这个常数C会变化为a[i]

具体实现代码(详解版):

#include <iostream>

using namespace std;

const int N = 1e6 + 10; 

int a[N], b[N]; // a 用于存储初始数组,b 为差分数组
int n, m; // n: 数组大小, m: 操作次数

// 插入操作,差分数组的更新
void insert(int l, int r, int c) {
    b[l] += c;       // 在区间左端点加上 c
    b[r + 1] -= c;   // 在区间右端点的下一个位置减去 c
}

int main() {
    cin >> n >> m;

    // 读取初始数组 a 的值,并将其转换为差分数组 b
    for (int i = 1; i <= n; i++) {
        cin >> a[i];      // 读入初始数组
    }
    for (int i = 1; i <= n; i++) {
        insert(i, i, a[i]);  // 将初始数组 a 构造成差分数组 b
    }
    
    // 处理 m 次操作
    while (m--) {
        int l, r, c;
        cin >> l >> r >> c; // 读入操作:对区间 [l, r] 加上 c
        insert(l, r, c);    // 更新差分数组
    }

    // 通过差分数组 b 的前缀和,恢复数组 a 的最终状态
    for (int i = 1; i <= n; i++) {
        b[i] += b[i - 1];  // 前缀和构造最终数组
    }

    // 输出更新后的数组
    for (int i = 1; i <= n; i++) {
        cout << b[i] << ' ';  
    }
    cout << endl; // 换行
    
    return 0;
}

4.二维差分

类比一维差分和二维前缀和,二维差分的作用:以时间复杂度O(1)对任意子矩阵加上一个常数
具体实现:

期望对矩阵a中左上角为[x1, y1],右下角为[x2, y2]的区域内的全部元素,都加一个常数C,则可以转化为对其差分矩阵b的操作。
先对b中[x1, y1]位置上的元素加C,这样以来,前缀和a中[x1, y1]这个点的右下角区域内的所有数都加上了C,但是这样就对[x2, y2]之后的区域也都加了C。 我们对[x2, y2]之外的区域需要保持值不变,所以需要进行减法。对b{x2+1,y1} 减掉C,这样下图红色区域都被减了C,再对b{x1,y2+1}减掉C,这样下图蓝色区域都被减了C,而红色区域和蓝色区域有重叠,重叠的区域被减了2次C,所以要再加回一个C,即对b{x2+1,y2+1}加上一个C。这样,就完成了对[x1,y1],[x2, y2]区域内的所有数(下图绿色区域),都加上常数C。
在这里插入图片描述
构造差分二维数组b
思想和一维差分一样,假设二维数组a和二维数组b初始化都为0,在输入a[i][j]时就可顺便构造b[i][j]实际就把构造数组b的过程看作为给一个数组中[x1, y1],[x2, y2]区域内的全部元素都加上一个常数C的过程,只不过特殊的是这个数组元素初始都为0,这个区域为[i,j]到[i,j](即就是一个元素),这个常数C会变化为a[i][j]

具体实现代码(详解版):

#include <iostream>

using namespace std;

const int N = 1010;

int a[N][N], b[N][N]; // a 用于存储原始矩阵,b 用于存储差分矩阵
int n, m, q;

// 插入操作:更新差分矩阵 b,反映在子矩阵 (x1, y1) 到 (x2, y2) 中加上常数 c 的操作
void insert(int x1, int y1, int x2, int y2, int c) {
    b[x1][y1] += c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main() {
    cin >> n >> m >> q;
    
    // 输入原始矩阵 a
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    
    // 构造差分矩阵 b,基于初始矩阵 a
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            insert(i, j, i, j, a[i][j]);
        }
    }
    
    // 根据查询进行更新操作
    while (q--) {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c);
    }
    
    // 通过计算二维前缀和来得到最终的矩阵
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            b[i][j] = b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1] + b[i][j];
        }
    }
    
    // 输出更新后的最终矩阵
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cout << b[i][j] << ' ';
        }
        cout << endl;
    }
    
    return 0;
}

以上就是前缀和差分的基本应用,前缀和除了在这里和差分一起使用外,后续还会有应用。(主要的就说要推导并记住一维和二维前缀和的公式)

s[i] = s[i] + a[i];//计算一维前缀和
res = s[r] - s[l-1];//计算l-r的区间和

s=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];//计算二维前缀和
res = s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]; 利用前缀和公式计算子矩阵的和

差分的重点是构造差分数组:

//一维
// 插入操作,差分数组的更新
void insert(int l, int r, int c) {
    b[l] += c;       // 在区间左端点加上 c
    b[r + 1] -= c;   // 在区间右端点的下一个位置减去 c
}
...
insert(i, i, a[i]);  // 将初始数组 a 构造成差分数组 b

//二维
// 插入操作:更新差分矩阵 b,反映在子矩阵 (x1, y1) 到 (x2, y2) 中加上常数 c 的操作
void insert(int x1, int y1, int x2, int y2, int c) {
    b[x1][y1] += c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y1] -= c;
    b[x2 + 1][y2 + 1] += c;
}
...
 insert(i, j, i, j, a[i][j]);// 构造差分矩阵 b,基于初始矩阵 a

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2213076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电力电子技术(一)

变压器漏感对整流电路的影响&#xff1a;

Find My微型电磨机|苹果Find My技术与电磨机结合,智能防丢,全球定位

微型电磨机是一种多功能电动工具&#xff0c;主要用于打磨、抛光、雕刻、钻孔等多种作业。‌ 它由控制箱和电磨笔两部分组成&#xff0c;通过直流稳压电源供电&#xff0c;电磨笔以直流马达为驱动源&#xff0c;带动磨头进行高速旋转机械运动&#xff0c;配合不同材质、形状的磨…

制药企业MES与TMS的数据库改造如何兼顾安全与效率双提升

*本图由AI生成 在全球制造业加速数字化转型的浪潮中&#xff0c;一家来自中国的、年营业额超过200亿元的制药企业以其前瞻性的视角和果断的行动&#xff0c;成为该行业里进行国产化改造的先锋。通过实施数据库改造试点项目&#xff0c;该企业实现了其关键业务系统MES&#xff0…

请求的响应----状态码分为五大类(爬虫)

前言 一个爬虫的成功与否&#xff0c;在于你是否拿到了想要的数据&#xff1b;一个请求的成功与否&#xff0c;在于响应的状态码&#xff0c;它标明了当前请求下这个响应的结果&#xff0c;是好还是坏。上节课程学习了HTTPS和HTTP协议的各自优势&#xff0c;本节课程进入到请求…

C++: AVL树的实现

一.AVL树的旋转 AVL树是平衡搜索二叉树的一种。 平衡因子&#xff1a;节点右树的高度减左树的高度&#xff0c;AVL树规定平衡因子的绝对值小于2。若不在这个范围内&#xff0c;说明该树不平衡。 AVL树节点&#xff1a; struct AVLTreeNode {AVLTreeNode(const T& data …

【AI 新观察】“转人工!转人工!”——智能客服痛点与破局之路

在当今数字化时代&#xff0c;智能客服在电商等众多领域被广泛应用&#xff0c;然而&#xff0c;一句又一句“转人工&#xff01;转人工&#xff01;”却常常暴露出智能客服存在的痛点。一、智能客服之痛 1. 理解偏差引不满 智能客服在理解客户问题时&#xff0c;常常出现偏差…

代码随想录 -- 回溯 -- 解数独

37. 解数独 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; class Solution(object):def back(self,board):for i in range(len(board)):for j in range(len(board[0])):if board[i][j] ! .:continuefor k in range(1,10):if self.isValid(i,j,k,board):board[i][j…

为什么要做自动化测试

一、自动化测试 自动化测试是把以人为驱动的测试行为转化为机器执行的一种过程。 个人认为&#xff0c;只要能服务于测试工作&#xff0c;能够帮助我们提升工作效率的&#xff0c;不管是所谓的自动化工具&#xff0c;还是简单的SQL 脚本、批处理脚本&#xff0c;还是自己编写…

10.13论文阅读

通过联合学习检测和描述关键点增强可变形局部特征 摘要 局部特征提取是计算机视觉中处理图像匹配和检索等关键任务的常用方法。大多数方法的核心理念是图像经历仿射变换&#xff0c;忽略了诸如非刚性形变等更复杂的效果。此外&#xff0c;针对非刚性对应的新兴工作仍然依赖于…

2024 年江苏省职业院校技能大赛“区块链技术应用” 赛项赛卷(样卷)运维题解析二

运维题 环境: ubuntu20 fisco 2.8.0 前言 准备两台机子,并且可以能相互pin通 192.168.19.133 [M1-A] 192.168.19.137 [M2-B] 子任务 1-2-3:区块链节点运维 基于已搭建的区块链系统与控制台,在机器(M1-A)上开展区块链群组与 节点的运维工作,具体内容如下: (1)基于…

Jenkins如何更改主目录文件夹?设置路径?

Jenkins如何更改主目录文件夹&#xff1f;设置路径&#xff1f;?简单几步&#xff0c;让你轻松解决。 工具/原料 联网电脑 方法/步骤 1. ssh连接到jenkins的服务器。使用root权限执行下面的命令&#xff0c;第一句是查看jenkins是否启用&#xff0c;启用的话需要第二个命令关…

肽合同制造(CDMO):北美和欧洲是全球最大肽合同制造(CDMO)消费地区

据 HengCe 最新调研&#xff0c;2023年中国肽合同制造&#xff08;CDMO&#xff09;市场销售收入达到了 万元&#xff0c;预计2030年可以达到 万元&#xff0c;2024-2030期间年复合增长率(CAGR)为 %。本研究项目旨在梳理肽合同制造&#xff08;CDMO&#xff09;领域产品系列&am…

【linux开发-驱动】-linux内核相关

开发板&#xff1a;STM32MP157 一、编译linux内核源码 编译完成以后就会在 arch/arm/boot 这个目录下生成一个叫做 uImage 的文件&#xff0c;uImage 就是 我们要用的 Linux 镜像文件。 Linux 编 译 的 时 候 需 要 设 置 目 标 板 架 构 ARCH 和 交 叉 编 译 器 CROSS_COMP…

确认:代码覆盖率是无用的管理指标

发现拆解代码覆盖率指标的简单证明 代码覆盖率是衡量软件产品质量的一个强有力的指标&#xff0c;多年来&#xff0c;技术领导者们对此深信不疑。从表面上看&#xff0c;其理由似乎很充分&#xff1a;测试越彻底&#xff0c;代码覆盖率就越高&#xff0c;因此&#xff0c;我们…

数据屏蔽与加密:代理用户需要了解的内容

您可能已经意识到数据安全和隐私的重要性。尽管存在各种方法来解决这两个问题&#xff1a;道德考量和监管要求&#xff0c;在本指南中&#xff0c;我们将重点介绍两种流行的策略&#xff1a;屏蔽和加密 - 以及它们的比较。 那么&#xff0c;哪个方法更胜一筹呢&#xff1f;答案…

时间序列预测(三)——激活函数(Activation Function)

激活函数是神经网络中每个神经元的输出函数&#xff0c;用于引入非线性&#xff0c;从而使神经网络能够逼近复杂的非线性关系。没有激活函数的网络只能表示线性变换&#xff08;如上一篇的线性回归不需要激活函数&#xff09;&#xff0c;因此不能解决实际中的非线性问题。激活…

【Python】从零到一,搭建高效Web服务器,轻松上手!Python开发者必备(文末附带源码分享)

CSDN Python源码分享&#xff1a;实现一个简单的Web服务器 在CSDN上&#xff0c;我们经常分享各种技术文章和源码&#xff0c;帮助开发者们不断提升自己的技能。今天&#xff0c;我将为大家分享一个使用Python实现的简单Web服务器源码。这个Web服务器能够处理基本的HTTP GET请…

Java项目实战II基于Java+Spring Boot+MySQL的桂林旅游景点导游平台(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者 一、前言 桂林&#xff0c;以其独特的喀斯特地貌、秀美的自然风光闻名遐迩&#xff0c;每年吸引着无数国内外游…

(04)python-opencv图像处理——图像阈值、平滑图像、形态转换、图像梯度

目录 前言 一、图像阈值 1.1 简单的阈值法 1.2 自适应阈值 二、平滑图像 2.1 二维卷积(图像滤波) 2.2 图像模糊 2.2.1均值模糊 2.2.2高斯模糊 2.2.3 中值滤波 2.2.4 双边滤波 三、形态转换 1、腐蚀 2、膨胀 3、开运算 4、闭运算 四、图像梯度 Sobel 和 Scharr …

【Pycharm系列】如何使用Windows的pycharm来远程连接linux做开发?

目录 前言一、原因二、步骤2-1、打开配置2-2、新建SFTP连接2-3、添加SSH连接信息2-4、配置连接信息2-5、构建连接2-6、打开远程项目文件目录2-7、配置项目依赖 总结 前言 使用Linux部署&#xff0c;使用Windows远程开发&#xff0c;可以提升开发效率&#xff0c;以及项目运行的…