数据结构-树状数组专题(2)

一、前言

接上回树状数组专题(1),这次主要介绍差分跟树状数组联动实现区间更新

二、我的模板

重新放了一遍,还是提一嘴,注意下标从0开始,区间左闭右开

template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n_ = 0) {
        init(n_);
    }

    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }

    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }

    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }

    int select(const T &k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::__lg(n); i; i /= 2) {
            if (x + i <= n && cur + a[x + i - 1] <= k) {
                x += i;
                cur = cur + a[x - 1];
            }
        }
        return x;
    }
};

三、差分简介

差分实际上是前缀和的逆运算,类似于函数求微积分实际上是函数求导函数的逆运算。

因此我们可以得到一个结论

差分数组的前缀和等于原数组,原数组的前缀和就等于前缀和数组

前缀和的差分等于原数组,原数组的差分就等于差分数组

我们回忆一下前缀和是怎么来的

prefix[i] = prefix[i-1]+a[i]

那么移项可以得到

a[i] = prefix[i]-prefix[i-1]

这就是差分数组的构造方式,此时的a数组就相当于差分数组,prefix数组相当于原数组

因此就有

for(int i = 1;i <= n;++i){
    prefix[i] = prefix[i-1]+a[i];
    diff[i] = a[i]-a[i-1];
}

其中diff数组就是a数组的差分数组

区间修改

因此为了实现区间修改,如果要把[l,r]这个区间的数都加上x,那么我们可以对构造出来的差分数组做这样的两个操作:
diff[l]+=x;diff[r+1]-=x;

这时候你要单点查询左端点l的值,实际上就是对diff[1]到diff[l]求前缀和,由于我们树状数组的特性,可以在O(logn)的时间复杂度下完成求前缀和,你会发现a[l]的的确确加上了x,再依次对[l,r]中的值实验,发现确实都增加了x,从r+1开始,后面的值还是保持原状不增不减,这就达到了我们需要的效果

原理分析:

diff[1] = a[1] - a[0]

diff[2] = a[2] - a[1]

diff[3] = a[3] - a[2]

...

diff[l] = a[l] - a[l-1]

...

diff[r] = a[r] - a[r-1]

diff[r+1] = a[r+1] - a[r]

...

diff[n] = a[n] - a[n-1]

对diff[l]做前缀和实际上等价于求diff[1]+diff[2]+diff[3]+...+diff[l]

=(a[1]-a[0])+(a[2]-a[1])+(a[3]-a[2])+...+(a[l]-a[l-1])=a[l]-a[0]=a[l]

因此如果diff[l]+=x的话

整体求前缀和就等价于diff[1]+diff[2]+diff[3]+...+(diff[l]+x)

=(a[1]-a[0])+(a[2]-a[1])+(a[3]-a[2])+...+(a[l]-a[l-1]+x)=a[l]+x-a[0]=a[l]+x

同理,也可验证diff[r+1]+x的情况

这里就不过多阐述了

因此我们用两个O(1)的操作实现了区间修改,取代了暴力修改的O(n)的时间复杂度

区间查询

由于我们采用差分数组来构造的树状数组,那么我们要求原数组修改后的区间和,原本是需要对差分数组做两遍前缀和的,但是我们这里可以尝试构造出两个树状数组来实现求区间和

如果要求[l,r]的区间和,我们就需要先学会计算前缀和,假设我们要求前k个数的前缀和

那么对于差分数组diff[]来说,我们枚举出来,应该需要计算

a[1] = diff[1]

a[2] = diff[1] + diff[2]

a[3] = diff[1] + diff[2] + diff[3]

...

a[k] = diff[1] + diff[2] + diff[3] + ... + diff[k]

似乎找不到什么有用的结论,我们不妨添加一行,再将剩余位置补全

a[0] = diff[1] + diff[2] + diff[3] + ... + diff[k]

a[1] = diff[1] + diff[2] + diff[3] + ... + diff[k]

a[2] = diff[1] + diff[2] + diff[3] + ... + diff[k]

a[3] = diff[1] + diff[2] + diff[3] + ... + diff[k]

...

a[k] = diff[1] + diff[2] + diff[3] + ... + diff[k]

这时候我们发现,我们要求的a[1]到a[k]的和,就等于这个矩形的和减去红色部分的和

因此前k个数的和(前缀和)为(k+1)*fenwick1.sum(k)-fenwick2.sum(k)

fenwick1表示第一个树状数组,fenwick2表示第二个树状数组

fenwick1中保存的是数组a的差分数组diff,fenwick2中保存的是i*diff[i](1<=i<=n)

这块还是以会写代码为主

四、专题训练

4.1 星码StarryCoding P41 树状数组(区间修改)

思路

这题是树状数组区间修改的模板题,主要要会写代码

我的代码

#include <bits/stdc++.h>
using i64 = long long;
constexpr int N = 2e5+5;

template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n_ = 0) {
        init(n_);
    }

    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }

    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }

    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }

    int select(const T &k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::__lg(n); i; i /= 2) {
            if (x + i <= n && cur + a[x + i - 1] <= k) {
                x += i;
                cur = cur + a[x - 1];
            }
        }
        return x;
    }
};

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n,q;
    std::cin >> n >> q;
    std::vector<i64> a(N,0),diff1(N,0),diff2(N,0);
    for(int i = 1;i <= n;++i) {
        std::cin >> a[i];
        diff1[i] = a[i]-a[i-1];
        diff2[i] = i*diff1[i];
    }
    Fenwick<i64> fenwick1(N),fenwick2(N);
    for(int i = 1;i <= n;++i) {
        fenwick1.add(i-1,diff1[i]);
        fenwick2.add(i-1,diff2[i]);
    }
    while(q--) {
        int op,l,r;i64 x;
        std::cin >> op >> l >> r;
        if(op==1) {
            std::cin >> x;
            fenwick1.add(l-1,x);
            fenwick2.add(l-1,l*x);
            fenwick1.add(r,-x);
            fenwick2.add(r,(r+1)*(-x));
        }else {
            i64 pre1 = fenwick1.sum(r)*(r+1)-fenwick2.sum(r);
            i64 pre2 = fenwick1.sum(l-1)*l-fenwick2.sum(l-1);
            std::cout << pre1-pre2 << '\n';
        }
    }
    
    return 0;
}

4.2 AcWing 242. 一个简单的整数问题

思路

跟上一题如出一辙,这个题还更加简单,因为只需要实现区间修改和单点查询,这就意味着我们只需要用一个树状数组就可以解决

我的代码

#include <bits/stdc++.h>
using i64 = long long;
constexpr int N = 1e5+5;

template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n_ = 0) {
        init(n_);
    }

    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }

    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }

    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }

    int select(const T &k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::__lg(n); i; i /= 2) {
            if (x + i <= n && cur + a[x + i - 1] <= k) {
                x += i;
                cur = cur + a[x - 1];
            }
        }
        return x;
    }
};

int main(){
    std::cin.tie(nullptr)->sync_with_stdio(false);
    
    int n,m;
    std::cin >> n >> m;
    std::vector<int> a(N,0);
    for(int i = 1;i <= n;++i){
        std::cin >> a[i];
    }
    std::vector<int> diff(N,0);
    for(int i = 1;i <= n;++i){
        diff[i] = a[i] - a[i-1];
    }
    Fenwick<int> fenwick(N);
    for(int i = 1;i <= n;++i){
        fenwick.add(i-1,diff[i]);
    }
    while(m--){
        std::string op;
        int l,r,d,x;
        std::cin >> op;
        if(op=="Q"){
            std::cin >> x;
            int ans = fenwick.sum(x);
            std::cout << ans << '\n';
        }else{
            std::cin >> l >> r >> d;
            fenwick.add(l-1,d);
            fenwick.add(r,-d);
        }
    }
    
    return 0;
}

4.3 AcWing 243. 一个简单的整数问题2

思路

跟4.1换个题目场景罢了,解题思路一模一样,甚至代码改几个地方就能拿过来AC掉

我的代码

#include <bits/stdc++.h>
using i64 = long long;
constexpr int N = 1e5+5;

template <typename T>
struct Fenwick {
    int n;
    std::vector<T> a;

    Fenwick(int n_ = 0) {
        init(n_);
    }

    void init(int n_) {
        n = n_;
        a.assign(n, T{});
    }

    void add(int x, const T &v) {
        for (int i = x + 1; i <= n; i += i & -i) {
            a[i - 1] = a[i - 1] + v;
        }
    }

    T sum(int x) {
        T ans{};
        for (int i = x; i > 0; i -= i & -i) {
            ans = ans + a[i - 1];
        }
        return ans;
    }

    T rangeSum(int l, int r) {
        return sum(r) - sum(l);
    }

    int select(const T &k) {
        int x = 0;
        T cur{};
        for (int i = 1 << std::__lg(n); i; i /= 2) {
            if (x + i <= n && cur + a[x + i - 1] <= k) {
                x += i;
                cur = cur + a[x - 1];
            }
        }
        return x;
    }
};

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n,m;
    std::cin >> n >> m;

    std::vector<i64> a(N,0),diff1(N,0),diff2(N,0);
    Fenwick<i64> fenwick1(N),fenwick2(N);
    for(int i = 1;i<=n;++i) {
        std::cin >> a[i];
        diff1[i] = a[i]-a[i-1];
        diff2[i] = i*diff1[i];
    }

    for(int i = 1;i<=n;++i) {
        fenwick1.add(i-1,diff1[i]);
        fenwick2.add(i-1,diff2[i]);
    }
    while(m--) {
        std::string op;int l,r;i64 d;
        std::cin >> op;
        if(op=="C") {
            std::cin >> l >> r >> d;
            fenwick1.add(l-1,d);
            fenwick2.add(l-1,l*d);
            fenwick1.add(r,-d);
            fenwick2.add(r,(r+1)*(-d));
        }else {//op=="Q"
            std::cin >> l >> r;
            i64 pre1 = fenwick1.sum(r)*(r+1)-fenwick2.sum(r);
            i64 pre2 = fenwick1.sum(l-1)*l-fenwick2.sum(l-1);
            std::cout << pre1-pre2 << '\n';
        }
    }
    
    return 0;
}

五、后记

等这几天回去再去看看线段树,后续可以类比一下线段树的写法和思路,线段树实际上更加全能,但是代码量真的太大,理解不透彻的人写着写着就Segment Fault了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QA|使用 MapleSim 模拟卷料生产 (Converting)和卷对卷系统 (R2R)

使用 MapleSim 模拟卷料生产 (Converting)和卷对卷系统 (R2R) 纸张、薄膜、塑料、金属箔、新能源电池和卷料生产设备 (converting equipment) 的制造商正在转向建模和仿真&#xff0c;以提升卷料处理的设备性能和产品质量。MapleSim 卷料处理库提供了专业的建模元件以及功能&a…

2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程

此套源码功能强大&#xff0c;支持APK脱壳、注入、网络验证、注册机、引流弹窗、更新弹窗和公告等功能&#xff0c;并具有强大的系统应用管理端&#xff0c;可轻松管理用户数量和卡密状态等数据统计。armpro脱壳软件可在线修改手机文件和游戏数据&#xff0c;并可添加会员功能、…

汉诺塔(hanio)--C语言函数递归

文章目录 前言一、汉诺塔的图解二、问题分析总结 前言 什么是汉诺塔&#xff1f; 汉诺塔(Tower of Hanoi)&#xff08;也称河内塔&#xff09;是有法国数学家爱德华卢卡斯于1883年发明的一道智力题。它源于印度的一个古老传说&#xff1a;大梵天创造世界的时候做了三根钻石柱子…

【MySQL】数据库精细化讲解:内置函数知识穿透与深度学习解析

前言&#xff1a;本节内容讲述mysql里面的函数的概念&#xff0c; 在mysql当中&#xff0c; 内置了很多函数工作。 这些函数丰富了我们的操作。 比如字符串函数、数据函数以及一些其他函数等等。 ps:友友们学习了表的基本操作后就可以观看本节内容啦! 目录 日期函数 current_…

Is:cannat access /data: Input/output error

说明&#xff1a; 1&#xff09;访问应用业务&#xff0c;输入账号密码报如下图所示&#xff1a;invalid login. 2&#xff09;登录服务器查看数据日志&#xff0c;报如下图所示&#xff1a;ls:cannot access /data: Input/output error 3&#xff09;查看日志dmesg |grep erro…

Python MySQL SQLServer操作

Python MySQL SQLServer操作 Python 可以通过 pymysql 连接 MySQL&#xff0c;通过 pymssql 连接 SQL Server。以下是基础操作和代码实战示例&#xff1a; 一、操作 MySQL&#xff1a;使用 pymysql python 操作数据库流程 1. 安装库 pip install pymysql2. 连接 MySQL 示例 …

迅为RK3562开发板直连电脑配置方法(无线上网)

概述 由于环境限制&#xff0c;笔记本电脑和开发板无法通过路由器连接起来&#xff0c;所以本文的目的是要实现笔记本电脑和虚拟机能够通过 WIFI 上网&#xff0c;并且开发板通过网线连接笔记本电脑和虚拟机在同一个网段内&#xff0c;最终实现 TFTP 或 NFS 来进行开发调试。 通…

Mono Repository方案与ReactPress的PNPM实践

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 Mono Repository方案与ReactPress的PNPM实践 在当今软件开发领域&#xff0c;Mono Repository&#xff08;简称Monorepo&#xff09;已成为一种流行的代码管理方式&#xff0c;特…

timedatectl命令修改时间和时区

1.默认情况下&#xff0c;Linux系统通常每64分钟进行一次NTP时间同步。但是&#xff0c;这可以通过编辑/etc/ntp.conf文件来修改。在/etc/ntp.conf中设置minpoll和maxpoll参数。 timedatectl可以用来查询和更改系统时间设定&#xff0c;同时可以设定和修改时区信息。 一、查…

基于Opencv的图像处理软件

目录 一、背景及意义介绍背景意义 二、概述一、背景及意义介绍背景意义 三、论文思路解决问题 四、复现过程&#xff08;一&#xff09;图像处理模块二&#xff09;图形界面模块&#xff08;一&#xff09;图像处理模块实现步骤&#xff08;二&#xff09;图形界面模块实现步骤…

HTML的自动定义倒计时,这个配色存一下

<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>自定义倒计时</title><style>* {mar…

私有化部署视频平台EasyCVR宇视设备视频平台如何构建视频联网平台及升级视频转码业务?

在当今数字化、网络化的时代背景下&#xff0c;视频监控技术已广泛应用于各行各业&#xff0c;成为保障安全、提升效率的重要工具。然而&#xff0c;面对复杂多变的监控需求和跨区域、网络化的管理挑战&#xff0c;传统的视频监控解决方案往往显得力不从心。 EasyCVR视频融合云…

MacOS通过X11转发远程运行virt-manager进行虚机分配

今天需要通过本地macbook机器连接远程物理机&#xff0c;执行虚机分配&#xff0c;现有文档仅提供window环境安装&#xff0c;如下整理Mac环境下的安装步骤 操作篇 前提条件 支持x11转发的terminal&#xff0c;我本地使用iTerm2&#xff1b;本地安装XQuartz&#xff0c;作为…

【每天学点AI】实战图像增强技术在人工智能图像处理中的应用

图像增强&#xff08;Image Enhancement&#xff09;是人工智能和计算机视觉中一项重要的技术&#xff0c;也是人工智能数据集预处理的一个重要步骤。它旨在提高图像的质量&#xff0c;使其在视觉上更加清晰、细节更丰富。这项技术在自动驾驶、医疗诊断、安防监控等领域有着广泛…

hbase mongodb hive starrocks比较

本文是在学习大数据的几个数据存储系统相关的组件所记录下来的&#xff0c;主要是不同组件的基础概念初步了解与对比。 NoSql 在大数据时代&#xff0c;虽然RDBMS很优秀&#xff0c;但是面对快速增长的数据规模和日渐复杂的数据模型&#xff0c;RDBMS渐渐力不从心&#xff0c…

STM32端口模拟编码器输入

文章目录 前言一、正交编码器是什么&#xff1f;二、使用步骤2.1开启时钟2.2配置编码器引脚 TIM3 CH1(PA6) CH2 (PA7)上拉输入2.3.初始化编码器时基2.4 初始化编码器输入2.5 配置编码器接口2.6 开启定时器2.7获取编码器数据 三、参考程序四、测试结果4.1测试方法4.2串口输出结果…

wireshark使用lua解析自定义协议

wireshark解析自定义协议 1.自定义的lua放入路径2.修改init.lua2.1 开启lua2.2 init.lua文件最后加入自己的lua文件位置&#xff0c;这里需要确保与自己的文件名相同 3.编写lua4.编写c抓包5.wireshark添加自定义协议如何加调试信息 1.自定义的lua放入路径 一般是自己软件的安装…

基于docker进行任意项目灵活发布

引言 不管是java还是python程序等&#xff0c;使用docker发布的优势有以下几点&#xff1a; 易于维护。直接docker命令进行管理&#xff0c;如docker stop、docker start等&#xff0c;快速方便无需各种进程查询关闭。环境隔离。项目代码任何依赖或设置都可以基本独立&#x…

友思特新闻 | 友思特荣获广州科技创新创业大赛智能装备行业赛初创组优胜企业!

2024年11月19日&#xff0c;第十三届中国创新创业大赛&#xff08;广东广州赛区&#xff09;暨2024年广州科技创新创业大赛智能装备行业赛颁奖典礼隆重举行。 赛事奖项介绍&#xff1a;广州科技创新创业大赛智能装备行业赛 第十三届“中国创新创业大赛&#xff08;广东广州赛区…

FreeRTOS——消息队列

目录 一、概念及其作用 1.1概念 1.2特点 1.3工作原理 二、相关API 2.1创建队列 2.2任务中写队列 2.3任务中读队列 2.4中断中写队列 2.5中断中读队列 三、实现原理 3.1消息队列控制块 3.2消息队列的创建 3.3消息的发送 3.3.1任务中发送 3.3.2中断中发送 3.4消息的…