树状数组讲解

news2024/9/27 23:32:23

树状数组

文章目录

  • 树状数组
    • 引入
    • 例题
      • AcWing241.楼兰图腾
        • 思路
        • 代码
      • AcWing 242. 一个简单的整数问题
        • 思路
        • 代码
      • AcWing 244. 谜一样的牛
        • 思路
        • 代码
    • 总结

引入

在这里插入图片描述
树状数组主要维护的是这样一个数据结构:
tr[x]表示以x为终点的长度为lowbit(x)的前缀和
对于树状数组主要就两种操作

  1. 对一段区间求解前缀和
    通过合并分解后的每一段长度为相应lowbit的tr[i]求解
    模板
def ask(x) :
	res = 0
	i = x
	while i :
		res += tr[i]
		i -= lowbit(i)
	return res
  1. 修改某个数
    通过对其每一段父节点(加上lowbit)进行操作。
def add(x, c) :
	i = x
	while i <= n :
		tr[i] += c
		i += lowbit(i)

应用:

差分
扩展
区间求和 单点修改
区间修改 单点查询
求逆序对

例题

AcWing241.楼兰图腾

在完成了分配任务之后,西部 314
来到了楼兰古城的西部。

相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(∧),他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。

西部 314
在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n
个点,经测量发现这 n
个点的水平位置和竖直位置是两两不同的。

西部 314
认为这幅壁画所包含的信息与这 n
个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn)
,其中 y1∼yn
是 1
到 n
的一个排列。

西部 314
打算研究这幅壁画中包含着多少个图腾。

如果三个点 (i,yi),(j,yj),(k,yk)
满足 1≤i<j<k≤n
且 yi>yj,yj<yk
,则称这三个点构成 V 图腾;

如果三个点 (i,yi),(j,yj),(k,yk)
满足 1≤i<j<k≤n
且 yi<yj,yj>yk
,则称这三个点构成 ∧ 图腾;

西部 314
想知道,这 n
个点中两个部落图腾的数目。

因此,你需要编写一个程序来求出 V 的个数和 ∧ 的个数。

输入格式
第一行一个数 n

第二行是 n
个数,分别代表 y1,y2,…,yn

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

数据范围
对于所有数据,n≤200000
,且输出答案不会超过 int64

y1∼yn
是 1
到 n
的一个排列。

输入样例:
5
1 5 3 2 4
输出样例:
3 4

思路

对于V形图腾来说,可以通过枚举每一个点作为最低点时,可组成的V形序列个数,最终求和得到最终答案。
基于单个点x,我们可以先从左到右的记录其左端大于x的点个数,同理可以记录其右端大于x的点个数,二者相乘即为结果。
每次都得查询之前所有点中小于(大于)a[x]的个数,再遍历后一个点前,需要将当前点加入查询的数据结构中。
区间查询、单点修改
树状数组呼之欲出。

代码

N = 200010
a = [0] * N

def lowbit(x) :
	return x & -x

def ask(x) :
	res = 0
	i = x
	while i :
		res += tr[i]
		i -= lowbit(i)
	return res

def add(x, c) :
	i = x
	while i <= n :
		tr[i] += c
		i += lowbit(i)

n = int(input())
a[1 : n + 1] = list(map(int, input().split()))
	
greater, lower = [0] * (n + 1), [0] * (n + 1)
tr = [0] * (n + 1)
# 从左到右,记录每个点左边大于/小于的情况
for i in range(1, n + 1) :
	x = a[i]
	greater[i] = ask(n) - ask(x)
	lower[i] = ask(x - 1)
	add(x, 1)

res1, res2 = 0, 0
tr = [0] * (n + 1)
# 从右到左求解结果
for i in range(n, 0, -1) :
	x = a[i]
	res1 += greater[i] * (ask(n) - ask(x))
	res2 += lower[i] * ask(x - 1)
	add(x, 1)

print(res1, res2)

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

给定长度为 N的数列 A,然后输入 M 行操作指令。

第一类指令形如 C l r d,表示把数列中第 l∼r
个数都加 d

第二类指令形如 Q x,表示询问数列中第 x
个数的值。

对于每个询问,输出一个整数表示答案。

输入格式
第一行包含两个整数 N
和 M

第二行包含 N
个整数 A[i]

接下来 M
行表示 M
条指令,每条指令的格式如题目描述所示。

输出格式
对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围
1≤N,M≤105
,
|d|≤10000
,
|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

思路

C表示对一定区间进行操作,Q表示对单点查询。
转化为差分
对差分数组的单点操作 ≡ \equiv 对原数组的区间操作
对差分数组的区间查询 ≡ \equiv 对原数组的单点查询

代码

N = 100010

a= [0] * N
tr = [0] * N

def lowbit(x) :
	return x & -x

def add(x, c) :
	i = x
	while i <= n :
		tr[i] += c
		i += lowbit(i)

def ask(x) :
	res = 0
	i = x
	while i :
		res += tr[i]
		i -= lowbit(i)
	return res
	
n, m = map(int, input().split())

a[1 : n + 1] = list(map(int, input().split()))
# 初始化差分树状数组
for i in range(1, n + 1) :
	add(i, a[i] - a[i - 1])

for i in range(m) :
	cmd = input()
	if cmd[0] == "Q" :
		x = int(cmd.split()[1])
		print(ask(x))
	else :
		l, r, c = map(int, cmd[2 :].split())
		add(l, c)
		add(r + 1, -c)

AcWing 244. 谜一样的牛

有 n 头奶牛,已知它们的身高为 1∼n
且各不相同,但不知道每头奶牛的具体身高。

现在这 n
头奶牛站成一列,已知第 i
头牛前面有 Ai
头牛比它低,求每头奶牛的身高。

输入格式
第 1
行:输入整数 n

第 2…n
行:每行输入一个整数 Ai
,第 i
行表示第 i
头牛前面有 Ai
头牛比它低。
(注意:因为第 1
头牛前面没有牛,所以并没有将它列出)

输出格式
输出包含 n
行,每行输出一个整数表示牛的身高。

第 i
行输出第 i
头牛的身高。

数据范围
1≤n≤105
输入样例:
5
1
2
1
0
输出样例:
2
4
5
3
1

思路

从后往前,最后一头牛知道自己比前 A n A_n An头牛高,则最后一头牛是 A n + 1 A_n+1 An+1高的牛。
倒数第二头牛,知道自己比前 A n − 1 A_{n-1} An1头牛高,则当前的牛是除去 A n + 1 A_n+1 An+1的高度外的 A n − 1 + 1 A_{n-1} + 1 An1+1的高度的牛。
由此可以递推得到所有结果。
这个过程中我们需要维护一个所有未确定高度的数据结构,我们需要对这个数据结构的操作是,查询某个高度是第几高,以及删除某点的高度。
树状数组!!!!

代码

N = 100010

tr = [0] * N
a = [0] * N

def lowbit(x) : return x & -x

def add(x, c) :
	i = x
	while i <= n :
		tr[i] += c
		i += lowbit(i)

def ask(x) :
	res = 0
	i = x
	while i :
		res += tr[i]
		i -= lowbit(i)
	return res

n = int(input())

for i in range(2, n + 1) :
	a[i] = int(input())

for i in range(1, n + 1) :
	add(i, 1)

res = [0] * (n + 1)
for i in range(n, 0, -1) :
	x = a[i] + 1
	# 找到一个满足第x的高度
	l, r = 0, n
	while l < r :
		mid = (l + r) >> 1
		if ask(mid) >= x :
			r = mid
		else :
			l = mid + 1
	res[i] = l
	add(l, -1)
	
for i in range(1, n + 1) :
	print(res[i])

总结

树状数组的题,一般需要发掘题目中需要的两种操作,这一点非常关键,剩下实现就肥肠煎蛋。

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

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

相关文章

机器学习系统架构的10个要素

这是一个AI赋能的时代&#xff0c;而机器学习则是实现AI的一种重要技术手段。那么&#xff0c;是否存在一个通用的通用的机器学习系统架构呢&#xff1f;在老码农的认知范围内&#xff0c;Anything is nothing&#xff0c;对系统架构而言尤其如此。但是&#xff0c;如果适用于大…

风力发电机组浪涌保护器安全防护方案

风机的庞大与危险高空作业注定了其在基建和维护中不易操作&#xff0c;风机设备的主电源、过程控制、网络与通讯、现场设备需要高等级的防雷浪涌保护器冲击保护&#xff0c;提高系统及设备的可靠性和可用性。风电场的主要发电设备风力发电机组“大风车”是风电场的主要发电设备…

【数据分析】缺失数据如何处理?pandas

本文目录1. 基础概念1.1. 缺失值分类1.2. 缺失值处理方法2. 缺失观测及其类型2.1. 了解缺失信息2.2. 三种缺失符号2.3. Nullable类型与NA符号2.4. NA的特性2.5. convert_dtypes方法3. 缺失数据的运算与分组 3.1. 加号与乘号规则3.2. groupby方法中的缺失值4. 填充与剔除4.1. fi…

什么是让ChatGPT爆火的大语言模型(LLM)

什么是让ChatGPT爆火的大语言模型(LLM) 更多精彩内容: https://www.nvidia.cn/gtc-global/?ncidref-dev-876561 文章目录什么是让ChatGPT爆火的大语言模型(LLM)大型语言模型有什么用&#xff1f;大型语言模型如何工作&#xff1f;大型语言模型的热门应用在哪里可以找到大型语言…

王道操作系统课代表 - 考研计算机 第一章 计算机概述 究极精华总结笔记

本篇博客是考研期间学习王道课程 传送门 的笔记&#xff0c;以及一整年里对 操作系统 知识点的理解的总结。希望对新一届的计算机考研人提供帮助&#xff01;&#xff01;&#xff01; 关于对 “计算机概述” 章节知识点总结的十分全面&#xff0c;涵括了《操作系统》课程里的全…

(五十七)插入数据时到底是如何维护好不同索引的B+树的?

之前我们已经给大家彻底分析清楚了MySQL数据库的索引结构了&#xff0c;大家都知道不同索引的结构是如何的&#xff0c;大致是如何建立的&#xff0c;然后搜索的时候是如何根据不同的索引去查找数据的。 那么今天我们来给大家彻底讲清楚&#xff0c;你在插入数据的时候&#x…

JavaSE学习笔记day17

零、 复习昨日 File: 通过路径代表一个文件或目录 方法: 创建型,查找类,判断类,其他 IO 输入& 输出字节&字符 try-catch代码 一、作业 给定路径删除该文件夹 public static void main(String[] args) {deleteDir(new File("E:\\A"));}// 删除文件夹public s…

openjdk:8镜像中安装工具命令的方法

文章目录前言一、获取openjdk:8环境1.下载并运行openjdk:82.获取当前镜像的基础环境信息3.更换源并安装软件二、更新Dockerfile1.增加命令2.尝试构建3.测试镜像是否可用总结前言 记录一下openjdk:8镜像安装命令的方法 同事使用openjdk:8作为基础镜像构建业务镜像&#xff0c;但…

蚂蚁CTO线五年测试开发,年后被优化,跟大家唠几句

夙兴夜寐&#xff0c;回顾从毕业出来到现在的这五年&#xff0c;也是在这里给大家抛砖引玉了。先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入蚂蚁金服&#xff0c;以“人员优化”的名义无情被裁员&#xff0c;之后跳槽到了有赞&#xff0c;一…

软件测试的主要工作内容是什么

平时说起程序员印象中大都是做Java、做前端、做后端&#xff0c;用着非常晦涩难懂的语言。在电脑前哐哐哐&#xff0c;没一会满屏代码显现出来。然而程序员并不全是印象中这样&#xff0c;还有一部分&#xff1a;他们不常写代码&#xff0c;主要去检查代码&#xff0c;是不是出…

人工智能前置研究-神经元/离子通道

专栏目录: 高质量文章导航_GZVIMMY的博客-CSDN博客 前言: 《生命是什么》:来自有序中的有序,非周期性晶体!有机体内在生命及其外部世界的相互作用,都能被精确的定律所概述,但前提是它自身必须有一个巨大结构,分子数目太少的话,定律也就不准确了。 《神秘的量子生命…

基于RT-Thread完整版搭建的极简Bootloader

项目背景Agile Upgrade: 用于快速构建 bootloader 的中间件。example 文件夹提供 PC 上的示例特性适配 RT-Thread 官方固件打包工具 (图形化工具及命令行工具)使用纯 C 开发&#xff0c;不涉及任何硬件接口&#xff0c;可在任何形式的硬件上直接使用加密、压缩支持如下&#xf…

Python抓取数据具体流程

之前看了一段有关爬虫的网课深有启发&#xff0c;于是自己也尝试着如如何过去爬虫百科“python”词条等相关页面的整个过程记录下来&#xff0c;方便后期其他人一起来学习。 抓取策略 确定目标&#xff1a;重要的是先确定需要抓取的网站具体的那些部分&#xff0c;下面实例是…

MySQL Buffer Pool 详解

1. Buffer Pool 概述 Buffer Pool 到底是什么&#xff1f;从字面上看是缓存池的意思&#xff0c;没错&#xff0c;它其实也就是缓存池的意思。它是MySQL当中至关重要的一个组件&#xff0c;可以这么说&#xff0c;MySQL的所有的增删改的操作都是在 Buffer Pool 中执行的。 但…

90%的人都不算会爬虫,这才是真正的技术,从0到高手的进阶

很多人以为学会了urlib模块和xpath等几个解析库&#xff0c;学了Selenium就会算精通爬虫了&#xff0c;但到外面想靠爬虫技术接点私活&#xff0c;才发现寸步难行。 龙叔我做了近20年的程序员&#xff0c;今天就告诉你&#xff0c;真正的爬虫高手应该学哪些东西&#xff0c;就…

相向双指针 leetcode 15 16 18 611

简单介绍 与同向双指针区别就是&#xff0c;同向查找的是子串最明显得区别就是两侧进行搜索比较快 基本得模板如下 while(left < right) {if( ){}else if(){}else{ // left与right得遍历while(){}} }具体以题目为例 两数之和比较简单 不展示了 leetcode 15 三数之和 这个…

NOC·核桃编程马拉松复赛低年级组 A 卷真题

1. 舞台区如下所示,角色足球的初始坐标为(0,0)。等待下图程序运行完毕后,足 球的坐标是多少?( ) A(100,100) B(-100,100) C(100,-100) D(0,0) 2. 亮亮设计了一个“击落陨石”的游戏。点击开始按钮后,陨石会出现在舞台区的随机位置,碰到炸弹后就会消失…

webrtc处理视频丢包的机制

1.摘要WebRTC是一个开源的实时交互式音频和视频通信框架。本文讨论了WebRTC中用于处理视频通信路径中数据包丢失的一些机制。讨论了各种系统细节&#xff0c;提出了一种基于时间层的自适应混合NACK/FEC方法。结果显示了该方法如何控制实时视频通信的质量权衡2.介绍WebRTC[1]是一…

特征工程——Tabular Data Features multimodal features

一、前言 机器学习时期&#xff0c;要花费大量的时间在特征设计上&#xff0c;好的输入数据可以让训练事半功倍。而有了深度学习后&#xff0c;神经网络可以自动实现特征提取&#xff0c;解放了手工(理论上是这样&#xff0c;实际也是要进行特征筛选的&#xff0c;因为在应用中…

小程序容器技术在微服务架构中的应用

随着移动互联网的发展&#xff0c;小程序已经成为了一种非常流行的应用方式&#xff0c;它可以在不安装任何应用的情况下&#xff0c;直接在移动终端设备&#xff08;如&#xff1a;App&#xff0c;iPad等&#xff09;中运行。微服务架构则是一种的分布式系统架构&#xff0c;可…