【算法 高级数据结构】树状数组:一种高效的数据结构(二)

news2024/12/24 9:02:37

🚀个人主页:为梦而生~ 关注我一起学习吧!
💡专栏:算法题、 基础算法、数据结构~赶紧来学算法吧
💡往期推荐
【算法基础 & 数学】快速幂求逆元(逆元、扩展欧几里得定理、小费马定理)
【算法基础】深搜
数据结构各内部排序算法总结对比及动图演示(插入排序、冒泡和快速排序、选择排序、堆排序、归并排序和基数排序等)
【算法 & 高级数据结构】树状数组:一种高效的数据结构(一)

上一篇文章我们介绍了树状数组这个数据结构,并且进行了其原理的数学推导,这篇文章基于上一篇文章,来讲一下这个数据结构在算法题中的应用

还不知道树状数组是什么的来看这篇文章:【算法 & 高级数据结构】树状数组:一种高效的数据结构(一)

题目来源:AcWing


文章目录

  • 0 通用模板
  • 1 单点修改,区间求和
  • 2 区间增减,单点查询
  • 3 区间增减,区间求和


0 通用模板

首先,通用模板先摆上

//lowbit
int lowbit(int x){
	return x & -x;
}

//修改操作
void add(int x, int c){
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

//查询操作
int sum(int x){
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

1 单点修改,区间求和

  • 例题
    在这里插入图片描述

输入格式
第一行一个数 n n n

第二行是 n n n
个数,分别代表 y 1 , y 2 , … , y n y_1,y_2,…,y_n y1y2,,yn

输出格式
两个数,中间用空格隔开,依次为 V 的个数和 的个数。

数据范围
对于所有数据, n ≤ 200000 n≤200000 n200000,且输出答案不会超过 int64。
y 1 ∼ y n y_1∼y_n y1yn 1 1 1 n n n 的一个排列。

输入样例

5
1 5 3 2 4

输出样例

3 4
  • 主要思路

因为两个符号的特点对应的每个点的纵坐标有这样的特点: V 意味着两边点的纵坐标高于当前点的纵坐标, 意味着两边的纵坐标小于当前的纵坐标。所以我们只需要遍历两次数组,记录每个点左右高于或低于当前点的纵坐标的点的数量,然后利用排列组合,计算出总数。

以下题解来源:https://www.acwing.com/solution/content/13818/

  1. 从左向右依次遍历每个数a[i],使用树状数组统计在i位置之前所有比a[i]大的数的个数、以及比a[i]小的数的个数。
    统计完成后,将a[i]加入到树状数组。
  2. 从右向左依次遍历每个数a[i],使用树状数组统计在i位置之后所有比a[i]大的数的个数、以及比a[i]小的数的个数。
    统计完成后,将a[i]加入到树状数组。
  • 代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;

const int N = 200010;
int n;
int a[N], tr[N];
int Greater[N], Lower[N];

int lowbit(int x){
    return x & -x;
}

//修改操作
void add(int x, int c){
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

//查询操作
int sum(int x){
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main(){
    cin >> n;
    
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    
    //将区间分成n份,看每一份左边有多少个大于它和小于它的
    for(int i = 1; i <= n; i++){
        int y = a[i];
        Greater[i] = sum(n) - sum(y);
        Lower[i] = sum(y - 1);
        add(y, 1);
    }
    
    //memset一下tr数组,倒着求每一份右边有多少个小于和大于它的
    memset(tr, 0, sizeof(tr));
    LL res1 = 0, res2 = 0;
    for(int i = n; i; i--){
        int y = a[i];
        res1 += Greater[i] * (LL)(sum(n) - sum(y));
        res2 += Lower[i] * (LL)sum(y - 1);
        add(y, 1);
    }
    
    cout << res1 << " " << res2 << endl;
    
    return 0;
}

2 区间增减,单点查询

主要思想:建立差分数组

  • 例题

在这里插入图片描述
数据范围
1 ≤ N , M ≤ 1 0 5 , 1≤N,M≤10^5, 1N,M105,
∣ d ∣ ≤ 10000 , |d|≤10000, d10000,
∣ A [ i ] ∣ ≤ 1 0 9 |A[i]|≤10^9 A[i]109

输入样例

10 5
1 2 3 4 5 6 7 8 9 10
Q 4
Q 1
Q 2
C 1 6 3
Q 2

输出样例

4
1
2
5
  • 主要思路

树状数组每次修改对应的是某个范围的前缀和的值的修改,所以如果需要在大数据量的情况下进行区间修改操作,大概率会TLE的。

但是我们想到,区间操作我们可以利用最基础的差分,使得区间操作优化成 O ( 1 ) O(1) O(1)的复杂度。

可以看到,将题目在原始组上的操作,转换到差分数组上,和树状数组解决的问题一致。

  • 代码
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;

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

int lowbit(int x){
    return x & -x;
}

void add(int x, int c){
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

LL quary(int x){
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

int main(){
    cin >> n >> m;
    
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        add(i, a[i] - a[i - 1]);
    }
    
    while(m--){
        char op;
        cin >> op;
        
        if(op == 'C'){
            int l, r, d;
            cin >> l >> r >> d;
            add(l, d), add(r + 1, -d);
        }else{
            int x;
            cin >> x;
            cout << quary(x)<< endl;
        }
    }
    
    return 0;
}

3 区间增减,区间求和

主要思想:建立差分数组+公式

  • 例题

在这里插入图片描述
数据范围
1 ≤ N , M ≤ 1 0 5 , 1≤N,M≤10^5, 1N,M105,
∣ d ∣ ≤ 10000 , |d|≤10000, d10000,
∣ A [ i ] ∣ ≤ 1 0 9 |A[i]|≤10^9 A[i]109

输入样例

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例

4
55
9
15
  • 主要思想

上一个应用虽然利用差分数组解决了区间操作的问题,但是区间操作完之后,利用差分数组不利于进行区间查询,所以需要进行一些推导,看看有什么性质可以利用。

数学推导见如下题解:https://www.acwing.com/solution/content/44886/
因此只需维护两个树状数组即可
一个是差分数组d[i]的树状数组tr[i],还有一个是i*d[i]的树状数组tri[i]

  • 代码
#include<iostream>
#include<algorithm>
using namespace std;

typedef long long LL;

const int N = 100010;
int a[N];   
LL tr1[N], tr2[N];  //tr1[i] : b[i]的前缀和  tr2[i] : i * b[i]的前缀和
int n, m;

int lowbit(int x){
    return x & -x;
}

void add(LL tr[], int x, LL c){
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

LL quary(LL tr[], int x){
    LL res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

LL prefix_sum(int x){
    return quary(tr1, x) * (LL)(x + 1) - quary(tr2, x);
}

int main(){
    cin >> n >> m;
    
    for(int i = 1; i <= n; i++){
        cin >> a[i];
        int b = a[i] - a[i - 1];
        add(tr1, i, (LL)b);
        add(tr2, i, (LL)i * b);
    }
    
    while(m--){
        char op;
        cin >> op;
        
        if(op == 'C'){
            int l, r, d;
            cin >> l >> r >> d;
            add(tr1, l, (LL)d), add(tr1, r + 1, (LL)-d);
            add(tr2, l, (LL)l * d), add(tr2, r + 1, (LL)(r + 1) * -d);
        }else{
            int l, r;
            cin >> l >> r;
            cout << prefix_sum(r) - prefix_sum(l - 1) << endl;
        }
    }
    
    
    return 0;
}

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

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

相关文章

kingbase 归档日志

开启归档 archive_mode on archive_commandtest ! -f /home/archive_kb/%f && cp %p /home/archive_kb/%f 注意&#xff1a;修改后需要重启&#xff0c;archive_command 这里设置的是 归档日志文件存储在 归档日志路径 /home/archive_kb 生成归档文件 手动切换 [kin…

茶饮品牌抖音账号规划流量运营策划方案

【干货资料持续更新&#xff0c;以防走丢】 茶饮品牌抖音账号规划流量运营策划方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 抖音运营资料合集&#xff08;完整资料包含以下内容&#xff09; 目录 冷启动期 1. 直播前期准备 - 进行DOUA/B测试&#xff0…

Unity-UGUI系统

UGUI是什么 UGUI是Unity引擎内自带的UI系统官方称之为:Unity Ul 是目前Unity商业游戏开发中使用最广泛的UI系统开发解决方案 它是基于Unity游戏对象的UI系统&#xff0c;只能用来做游戏UI功能 不能用于开发Unity编辑器中内置的用户界面 六大基础组件 概述 Canvas EventS…

Java基于微信小程序的助农扶贫系统的研究与实现

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#…

远程控制手机关闭空调,轻松省下一笔电费!存起来夏天用

随着科技的不断发展&#xff0c;人们对于节能减排的需求也越来越高。在这个过程中&#xff0c;远程操控空调成为了一种新兴的节能技巧。通过远程操控空调&#xff0c;我们可以更加精确地控制室内温度&#xff0c;从而实现节能减排的目标。本文将详细介绍远程操控空调的优势以及…

3.25C++

定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定义一个全局函数完成对该字符串的反转 #include <iostream> #include <cstring> usi…

Linux 服务升级:Nginx 热升级 与 平滑回退

目录 一、实验 1.环境 2.Kali Linux 使用nmap扫描CentOS 3.Kali Linux 远程CentOS 4.Kali Linux 使用openvas 扫描 CentOS 5.Nginx 热升级 6.Nginx 平滑回退 二、问题 1.kill命令的信号有哪些 2.平滑升级与回退的信号 一、实验 1.环境 &#xff08;1&#xff09;主机…

电路笔记 :嘉立创EDA常用功能

原理图 翻转和旋转 嘉立创连线快捷键&#xff1a;Alt W 原理图转PCB 交叉选择&#xff08;切换至PCB并高亮选中的部分&#xff09;快捷键&#xff1a;“SHIFT X” PCB pcb边框 画pcb边框&#xff1a;放置-》板框-》矩形 挖槽 布线 pcB检查 出错的地方会有小叉子 等长调节…

NKCTF--pwn--Maimai查分器

NKCTF–pwn–Maimai查分器 Maimai查分器 保护全开 存在格式化字符串漏洞 第一步&#xff1a;先测速率&#xff0c;输入15.0 SSS 50次获得最高速率 ​ sl(b1) #debug() for i in range(50):sl(b15.0 SSS)然后利用格式化字符串去泄露&#xff0c;本来想一口气全部泄露的&…

继承和多态(2)(多态部分)

提前讲的重要知识点 一个类在没有父类的情况下默认有一个父类为Object类。 而当在有父类情况下&#xff0c;如果你那父类没有父类&#xff0c;则其父类的父类默认为object类&#xff0c;所以即使一个类有父类&#xff0c;其内部还是有object类。 object类都是隐藏起来的&…

浅谈C++引用的使用以及底层原理

文章目录 1、引用概念2、引用特性3、常引用4、引用的使用场景&#xff08;1&#xff09;做参数&#xff08;2&#xff09;做返回值 1、引用概念 引用不是新定义一个变量&#xff0c;而是给已存在变量取了一个别名&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和…

链式二叉树经典OJ题目(一)

目录 结构体声明&#xff1a; 1.单值二叉树 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 2.二叉树最大深度 题目描述&#xff1a; 思路分析&#xff1a; 源码&#xff1a; 3.检查两棵树是否相同 题目描述&#xff1a; 思路分析&#xff1a; 源码…

EMC | 浪涌保护电路NTC

NTC(5D-11)负温度系数热敏电阻。 NTC是过流器件。 抑制开机的浪涌电流&#xff0c;NTC温度越高&#xff0c;电阻越低。 如果没有NTC,220VAC开机对电容CE3充电&#xff0c;此时电容中没有电荷&#xff0c;CE3相当于短路&#xff0c;回路电流会很大。 选型注意 1、R25温度下电阻…

Negative Sampling with Adaptive DenoisingMixup for Knowledge Graph Embedding

摘要 知识图嵌入(Knowledge graph embedding, KGE)的目的是通过对比正负三元组&#xff0c;将知识图中的实体和关系映射到一个低维、密集的向量空间中。在kge的训练过程中&#xff0c;由于kge只包含正三元组&#xff0c;因此负采样对于找到高质量的负三元组至关重要。大多数现…

鸿蒙NXET实战:高德地图定位SDK【获取Key+获取定位数据】(二)

如何申请key 1、创建新应用 进入[控制台]&#xff0c;创建一个新应用。如果您之前已经创建过应用&#xff0c;可直接跳过这个步骤。 2、添加新Key 在创建的应用上点击"添加新Key"按钮&#xff0c;在弹出的对话框中&#xff0c;依次&#xff1a;输入应用名名称&…

04、JS实现:用⼆分法思想实现搜索旋转排序数组(一步一步剖析,很详细)

搜索旋转排序数组 Ⅰ、搜索旋转排序数组&#xff1a;1、题目描述&#xff1a;2、解题思路&#xff1a;3、实现代码&#xff1a; Ⅱ、小结&#xff1a; Ⅰ、搜索旋转排序数组&#xff1a; 1、题目描述&#xff1a; 给你⼀个升序排列的整数数组 nums &#xff0c;和⼀个整数 tar…

【刷题】滑动窗口精通 — Leetcode 30. 串联所有单词的子串 | Leetcode 76. 最小覆盖子串

送给大家一句话&#xff1a; 充满着欢乐与斗争精神的人们&#xff0c;永远带着欢乐&#xff0c;欢迎雷霆与阳光。 —— 赫胥黎 滑动窗口精通 前言Leetcode 30. 串联所有单词的子串题目描述算法思路 Leetcode 76. 最小覆盖子串题目描述算法思路 Thanks♪(&#xff65;ω&#xf…

产品推荐 | 基于 Zynq UltraScale+ XCZU27DR的 FACE-RFSoC-C高性能自适应射频开发平台

一、产品概述 FACE-RFSOC-C自适应射频开发平台&#xff0c;是FACE系列新一代的产品。 平台搭载有16nm工艺的Zynq UltraScale™ RFSoC系列主器件。该器件集成数千兆采样RF数据转换器和ARM Cortex-A53处理子系统和UltraScale可编程逻辑&#xff0c;是一款单芯片自适应射频平台。…

风力发电模型Windpowerlib概述与入门

Windpowerlib 是一个提供了一系列函数和类的库&#xff0c;用于计算风力涡轮机的功率输出。它最初是 feedinlib&#xff08;风能和光伏&#xff09;的一部分&#xff0c;但后来被分离出来&#xff0c;以建立一个专注于风能模型的社区。 这个库的主要目的是简化风力涡轮机的能量…

Vue3 + Vite + TS + Element-Plus + Pinia项目(3)--新建路由

1、在src文件夹下新建router文件夹后&#xff0c;创建index.ts文件 2、具体如下 import { createRouter, createWebHashHistory } from vue-routerconst router createRouter({history: createWebHashHistory(),routes: [{path: "/index",component: () > impor…