树状数组【数据结构】

树状数组

简介

1.应用

1.单点修改区间查询
2.区间修改单点查询(差分)
3.区间修改区间查询(差分+公式)
总而言之,就是动态维护前缀和。

2.树状结构图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3.lowbit函数

我们知道,任何一个正整数都可以被表示成一个二进制数。如:

( 2 ) 10 = ( 10 ) 2 (2)_{10}=(10)_2 (2)10=(10)2
( 4 ) 10 = ( 100 ) 2 (4)_{10}=(100)_2 (4)10=(100)2

那么定义一个函数 f = l o w b i t ( x ) f=lowbit(x) f=lowbit(x),这个函数的值是 x x x二进制表达式中最低位的 1 1 1所对应的值。

比如:

( 6 ) 10 = ( 110 ) 2 (6)_{10}=(110)_2 (6)10=(110)2
那么 l o w b i t ( 6 ) lowbit(6) lowbit(6)就等于 2 2 2,因为 ( 110 ) 2 (110)_2 (110)2中最低位(就是从右往左数的第二位)对应的数是 2 1 = 2 2^1=2 21=2

所以假设一个数的二进制最低位的 1 1 1在从右往左数的第 k k k位,那么它的 l o w b i t lowbit lowbit值就是

2 k − 1 2^{k−1} 2k1

x & − x x \& -x x&x的原理
我们都知道机一个数在计算机中是以补码的方式存储的

而负数的补码有这样一种求法:先求出这个数的正数的补码,然后从低位开始知道第一个 1 1 1之前(包括这个 1 1 1)的所有数不变,后面的所有数取反。

现在再来看 x & − x x\&-x x&x,我们发现,对 x x x − x -x x进行与运算之后,二进制下低位第一个 1 1 1之前的所有数(包括这个 1 1 1本身)不变,而高位所有数为0(一个数与上自己的取反的数)。

此时两个数与运算后得到的二进制数是: . . . 1... ...1... ...1...

就正是二进制表达式中最低位的 1 1 1所对应的值。

4. a d d add add 函数

a d d add add 函数中,我们是通过 x + l o w b i t ( x ) x+lowbit(x) x+lowbit(x) 取到 x x x 的父节点的。原理是什么呢?
首先, x x x 的父节点一定是树中高度比 x x x 高的节点中最矮的那个节点。
例如 6 6 6 的父节点是 8 8 8 而不是 16 16 16(高度比 8 8 8 高),也不是 7 7 7 (高度比 6 6 6 低)。
而树的高度是由 l o w b i t ( x ) lowbit(x) lowbit(x) 决定的。
因此, x x x 的高度就是 l o w b i t ( x ) lowbit(x) lowbit(x),比它高的节点中高度最低的节点的高度假设为 l o w b i t ( j ) lowbit(j) lowbit(j),那么 l o w b i t ( j ) lowbit(j) lowbit(j) 肯定就是 x + l o w b i t ( x ) x+lowbit(x) x+lowbit(x)
他满足高度比 l o w b i t ( x ) lowbit(x) lowbit(x) 高,因为, x + l o w b i t ( x ) x+lowbit(x) x+lowbit(x) 会使得 x x x 的最低位的 1 1 1 一定更高了。
其次,他也满足最低。结合二进制理解,例如 x = 01010 x=01010 x=01010 l o w b i t ( x ) = 2 lowbit(x)=2 lowbit(x)=2,那么我们肯定希望 l o w b i t ( j ) = 100 = 4 lowbit(j)=100=4 lowbit(j)=100=4
此时 x + l o w b i t ( x ) = 01100 x+lowbit(x)=01100 x+lowbit(x)=01100,其 l o w b i t ( ) = 100 lowbit()=100 lowbit()=100




应用一:单点修改,区间查询

PROCESS1: 题目链接

题目大意:求形如“山峰”或者“山谷”状三元组的数量

PROCESS2:思路

  1. 从暴力出发 O ( N 2 ) O(N^2) O(N2)

我们可以预处理出来每个点左侧和右侧大于和小于它的点的数量,然后相乘即可(乘法原理)

  1. 树状数组 O ( N l o g 2 N ) O(Nlog_2^N) O(Nlog2N)

树状数组中一种常用的求小于或者与某一个点的数量的方法:将原数组的权值映射到该点在树状数组的下标(经常与离散化结合使用)。例如 t r [ 3 ] = 5 tr[3]=5 tr[3]=5就表示权值为 3 3 3的点有 5 5 5个。这样在树状数组中求小于某一个数y的数的数量,就可以直接求 y y y之前的前缀和,这个前缀和就代表所有小于 y y y的数的个数和。
但这样就无法同时预处理一个点左侧小于它的点的数量和右侧小于它的点的数量,所以我们需要操作两侧,第一次预处理左侧的,然后第二次处理右侧时直接求结果即可。

注意答案可能爆int

PROCESS3:代码

点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 200010;int n, a[N], g[N], l[N];
int tr[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 get(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 ++ )   cin >> a[i];for(int i = 1; i <= n; i ++ ){int y = a[i];g[i] = get(n) - get(y);l[i] = get(y); add(y, 1);}memset(tr, 0, sizeof tr);long long res1 = 0, res2 = 0;for(int i = n; i >= 1; i -- ){int y = a[i];res1 += (long long)(get(n) - get(y)) * g[i];//这里会爆intres2 += (long long)get(y) * l[i];add(y, 1);}cout << res1 << ' ' << res2 << endl;return 0;
}



应用二:区间修改,单点查询

PROCESS1:例题链接

PROCESS2:思路

把树状数组看做差分数组,差分数组的前缀和就是某个点的权值

PROCESS3:代码

点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 100010;int n, m;
int tr[N], a[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 >> m;for(int i = 1; i <= n; i ++ )   cin >> a[i];for(int i = 1; i <= n; i ++ )   add(i, a[i] - a[i - 1]);while (m -- ){string op;cin >> op;if(op == "C"){int l, r, c;cin >> l >> r >> c;add(l, c);add(r + 1, -c);}else{int x;cin >> x;cout << sum(x) << endl;}}return 0;
}



应用三:区间修改,区间查询

PROCESS1:题目链接

PROCESS2:思路

由于涉及到区间修改,那么我们只能用树状数组模拟差分数组,否则区间修改的时间复杂度必然是 O ( N ) O(N) O(N)的。
此时,区间[1, n]的和为蓝色区域所示(b为树状数组)(a为原数组)
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如果我们把该蓝色区域的补集(红色区域加上)
总的矩阵就是: ( n + 1 ) ∗ ∑ 1 n b i = ( n + 1 ) ∗ a n (n + 1) * \sum_{1}^{n}b_i = (n + 1) * a_n (n+1)1nbi=(n+1)an
再减去红色区域: ∑ 1 n i ∗ b i \sum_{1}^{n}i*b_i 1nibi(竖着看)
就是我们的蓝色区域,也就是区间[1,n]的前缀和
那么,我们只需要在维护一个树状数组,tr[i]=i*b[i]即可

PROCESS3:代码

点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;typedef long long LL;const int N = 100010;int n, m;
LL tr1[N], tr2[N];
int a[N];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 sum(LL tr[], int x)
{LL res = 0;for(int i = x; i; i -= lowbit(i))   res += tr[i];return res;
}LL get(int x)
{return (long long)(x + 1) * sum(tr1, x) - sum(tr2, x); 
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ )   cin >> a[i];for(int i = 1; i <= n; i ++ )  {add(tr1, i, a[i] - a[i - 1]);add(tr2, i, (long long)i * (a[i] - a[i - 1]));}while (m -- ){string op;cin>> op;if(op == "C"){int l, r, c;cin >> l >> r >> c;add(tr1, l, c);add(tr1, r + 1, -c);add(tr2, l, (long long)l * c);add(tr2, r + 1, (long long)-c * (r + 1));}else{int l, r;  cin >> l >> r;cout << get(r) - get(l - 1) << endl;}}return 0;
}



其他例题

例题一

PROCESS1:Acwing
PROCESS2:思路

凡是这类一开始时,所求答案完全未知的问题,应该在边界问题上寻找原问题的突破口。因为对于那些靠近问题空间“中部”的子问题,其“左右两边”可以认为和它是**本质相同的子问题,没有办法直接解决。**因此我们应该着重考虑边界处的子问题。

在本题中,我们首先考虑最后一头牛。由于它已经统计了在它之前的所有牛,因此,假如它比 x x x头牛高,则它的高度一定是 x + 1 x+1 x+1(所有牛的高度是一个 [ 1 , n ] [1, n] [1,n]的排列)。我们采取从后往前考虑的方式,就是因为题目给出了“每头牛已知在自己前面比自己矮的牛的个数”这一条件,从后往前可以少考虑很多问题。

由于每头牛的高度各不相同且取遍区间 [ 1 , n ] [1,n] [1,n],因此,对于倒数第二头牛而言,它应该在除去上述 x + 1 x+1 x+1的区间[ [ 1 , n ] [1,n] [1,n]中,选取 A n − 1 + 1 A_{n−1}+1 An1+1小的数。其他的牛以此类推。

我们可以令树状数组的 t r tr tr表示当前这个数有没有被选,然后二分查询(不然时间复杂度还是 O ( N 2 ) O(N^2) O(N2)的)在没有被选的数当中第 A i + 1 A_{i}+1 Ai+1大的数。初始化 t r [ i ] = 0 , ( i ∈ 1 , n ) tr[i]=0,({i \in {1,n}}) tr[i]=0,(i1,n)表示当前数没有被选,选择一个数之后就把当前数置为 0 0 0

PROCESS3:代码

点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>using namespace std;const int N = 100010;int n, tr[N], h[N];
int res[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 >= 1; i -= lowbit(i))  res += tr[i];return res;
}int main()
{cin >> n;for(int i = 2; i <= n; i ++ )   cin >> h[i];for(int i = 1; i <= n; i ++ )   add(i, 1);for(int i = n; i >= 1; i -- ){int l = 0, r = n, goal = h[i] + 1;while(l < r){int mid = l + r >> 1;if(sum(mid) >= goal)    r = mid;else    l = mid + 1;}res[i] = r;add(r, -1);}for(int i = 1; i <= n; i ++ )   cout << res[i] << endl;return 0;
}

例题二

PROCESS1: 蓝桥杯




例题三:

PROCESS1: 蓝桥杯

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

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

相关文章

pytorch+maskRcnn框架训练自己的模型以及模型导出ONXX格式供C++部署推理

背景 maskrcnn用作实例分割时&#xff0c;可以较为精准的定位目标物体&#xff0c;相较于yolo只能定位物体的矩形框而言&#xff0c;优势更大。虽然yolo的计算速度更快。 直接开始从0到1使用maskrCNN训练自己的模型并并导出给C部署&#xff08;亲测可用&#xff09; 数据标注…

PCL配置

1、下载 打开GitHub网站&#xff0c;搜索pcl&#xff0c;选择第一个结果打开&#xff0c;按照下图步骤操作 下载PCL预编译安装程序PCL-1.13.1-AllInOne-msvc2022-win64.exe 和要安装的PCL组件&#xff08;例如pcl-1.13.1-pdb-msvc2022-win64.zip&#xff09; 2、安装 双击 P…

大模型tokenizer重构流程

大模型tokenizer层再训练&#xff08;选取Qwen7B试验&#xff0c;重构token层&#xff09; 最近公司可能想训练一个蛋白质大模型&#xff0c;需要了解一下大模型tokenizer重构&#xff0c;之后可能要训练&#xff0c;这里做了一定的总结。 文章目录 1. 首先查看Qwen2.5 7B基本…

Android设计模式之单例模式

一、定义&#xff1a;确保一个类只有一个实例&#xff0c;并且自动实例化&#xff0c;并向整个系统提供这个实例。 二、使用场景&#xff1a;避免重复创建对象&#xff0c;过多消耗系统资源。 三、使用方式 3.1饿汉式&#xff1a;类加载时立即初始化&#xff0c;线程安全&…

docker ssh远程连接

目录 操作命令&#xff1a; 确保 SSH 配置允许 root 登录&#xff1a; docker提交&#xff1a; 操作命令&#xff1a; # 进入容器 docker exec -ti lbg04 /bin/bash# 更新包管理并安装 SSH 服务&#xff08;Ubuntu/Debian 示例&#xff09; apt-get update apt-get install…

关于matlab和python谁快的问题

关于matlab和python谁快的问题&#xff0c;python比matlab在乘法上快10倍&#xff0c;指数计算快4倍&#xff0c;加减运算持平&#xff0c;略慢于matlab。或许matlab只适合求解特征值。 import torch import timen 50000 # 矩阵规模 M torch.rand(n, 31)start_time time.t…

准确--配置服务器文件数

某些系统可能在 /etc/security/limits.d/ 目录下有额外配置覆盖全局设置。检查是否存在冲突文件&#xff1a; ls /etc/security/limits.d/如果有文件&#xff08;如 90-nproc.conf 或 90-nofile.conf&#xff09;&#xff0c;需编辑或删除这些文件中的冲突配置。 确保系统启用…

VectorBT:使用PyTorch+LSTM训练和回测股票模型 进阶一

VectorBT&#xff1a;使用PyTorchLSTM训练和回测股票模型 进阶一 本文介绍了如何使用PyTorch和LSTM模型进行股票数据的训练和回测。涵盖了数据预处理、特征选择、LSTM模型构建、模型训练与验证、动态阈值策略生成交易信号以及使用VectorBT进行回测和绩效分析。 文中内容仅限技术…

mysql中的聚簇索引,什么是聚簇索引和非聚簇索引

文章目录 1. 什么是聚簇索引2. 非聚簇索引3. 聚簇索引的优缺点4. 聚簇索引的使用场景5. 聚簇索引和主键索引的异同前言: 在继续讲解专栏内容之前,先学习几个概念,以便更好了解: 什么是聚簇索引什么是回表这篇文章详细分析 聚簇索引。回表的理解可以进入这篇文章:什么是回表…

MantisBT在Windows10上安装部署详细步骤

MantisBT 是一款基于 Web 的开源缺陷跟踪系统&#xff0c;以下是在 Windows 10 上安装部署 MantisBT 的详细步骤&#xff1a; 1. 安装必要的环境 MantisBT 是一个基于 PHP 的 Web 应用程序&#xff0c;因此需要安装 Web 服务器&#xff08;如 Apache&#xff09;、PHP 和数据…

深入理解K8s与Docker的关系:容器化技术的双雄

友情提示&#xff1a;本文内容由银河易创&#xff08;https://ai.eaigx.com&#xff09;AI创作平台gpt-4-turbo模型生成&#xff0c;仅供参考。 在现代云计算及微服务架构的发展中&#xff0c;Docker与Kubernetes&#xff08;K8s&#xff09;作为两大核心技术&#xff0c;被广泛…

蓝桥与力扣刷题(蓝桥 蓝桥骑士)

题目&#xff1a;小明是蓝桥王国的骑士&#xff0c;他喜欢不断突破自我。 这天蓝桥国王给他安排了 N 个对手&#xff0c;他们的战力值分别为 a1,a2,...,an&#xff0c;且按顺序阻挡在小明的前方。对于这些对手小明可以选择挑战&#xff0c;也可以选择避战。 身为高傲的骑士&a…

如何查看window电脑的GPU信息

GPU&#xff08;图形处理器&#xff0c;Graphics Processing Unit&#xff09;和显卡是两个密切相关但不同的概念 概念 1. ‌基本概念‌ ‌GPU‌&#xff1a;是专门用于处理图像和视频信息的微处理器&#xff0c;拥有强大的并行计算能力&#xff0c;主要负责图形渲染、数值分…

26考研——查找_树形查找_二叉排序树(BST)(7)

408答疑 文章目录 三、树形查找二叉排序树&#xff08;BST&#xff09;二叉排序树中结点值之间的关系二叉树形查找二叉排序树的查找过程示例 向二叉排序树中插入结点插入过程示例 构造二叉排序树的过程构造示例 二叉排序树中删除结点的操作情况一&#xff1a;被删除结点是叶结点…

【数据库事务、消息队列事务、Redis 事务、Spring 事务 详细分析】

数据库事务、消息队列事务、Redis 事务、Spring 事务** 的详细分析 在分布式系统和应用开发中&#xff0c;事务管理是确保数据一致性和可靠性的关键机制。以下是针对 数据库事务、消息队列事务、Redis 事务、Spring 事务 的详细分析&#xff0c;包括原理、特点、适用场景和对比…

kubectl 命令参数详解与示例

kubectl 命令参数详解与示例 kubectl 是 Kubernetes 的命令行工具&#xff0c;用于与 Kubernetes 集群交互。下面我将详细介绍 kubectl 的主要命令参数&#xff0c;并提供相应的使用示例。 一、基础命令 1. kubectl get - 获取资源信息 常用参数&#xff1a; -n, --namesp…

#vue中解决异步请求的竞态

// composables/useFetchWithoutRace.js import { ref } from vue; import axios from axios;// 定义一个可复用的 Composition 函数&#xff0c;处理带有竞态控制的异步请求 export function useFetchWithoutRace() {// 定义响应式变量 latestRequestId&#xff0c;用于追踪最…

如何在 Postman 中导入和导出 cURL 命令?

cURL 是一款广受欢迎的命令行工具&#xff0c;专门用于执行 HTTP 请求。它在 Web 应用或 API 测试中极为实用&#xff0c;让用户得以借助在 API 开发者社区广为流行的成熟语法&#xff0c;直接通过命令行与 API 进行交互。若你需要在多个环境下运行众多 cURL 命令&#xff0c;可…

用python制作一个贪吃蛇小游戏

文章目录 效果图python源码使用说明效果图 只需要一百多行python代码,就能制作一个贪吃蛇小游戏。效果如下: 操作说明: 你可以使用上下左右箭头键来控制蛇的移动方向。蛇吃到食物后会变长,当蛇撞到墙壁或自己的身体时游戏结束。游戏结束后,你可以按 Q 退出游戏,或按 C…

react 15-16-17-18各版本的核心区别、底层原理及演进逻辑的深度解析

一、React 15&#xff08;2016&#xff09; 核心架构&#xff1a;Stack Reconciler&#xff08;栈协调器&#xff09; 工作原理&#xff1a; 同步递归渲染&#xff1a;采用深度优先遍历方式递归处理 Virtual DOM&#xff0c;形成不可中断的调用栈渲染流程&#xff1a;1. 触发 …