树状数组(入门附模板)

news2024/11/15 13:49:44

声明:本篇文章图片非原创 

目录

简介

lowbit函数

结构分析

单点修改,区间查询

区间修改,单点查询

区间修改,区间查询

模板题

树状数组1–单点修改,区间查询

题目描述

输入格式 

输出格式

输入输出样例

输入 #1

输出 #1

说明/提示

分析

代码

树状数组2––区间修改,单点查询

题目描述

输入格式

输出格式

输入输出样例

输入 #1

输出 #1

说明/提示

样例 1 解释:

数据规模与约定

分析

代码

最强编译器推荐


简介

        树状数组二叉索引树(英语:Binary Indexed Tree),又以其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以A New Data Structure for Cumulative Frequency Tables为题发表在SOFTWARE PRACTICE AND EXPERIENCE。其初衷是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和。        ——源自百度百科

        百度百科太过于学术化,看不懂?怎么办?总的来说,树状数组是一种支持单点修改区间查询的一种代码量小的数据结构。

        那么树状数组和线段树有什么区别呢?

  1. 线段树的码量很明显比树状数组多
  2. 线段树可以进行区间修改和区间查询,然而树状数组是线段树的低级版本,可以进行区间查询,但只能一次性改一个点,当然,经过改造后的树状数组可以进行区间修改,我们今天先讲树状数组的低级版本。

如上图,这就是树状数组的形式,仔细看一看,你会发现树状数组是不规则的叉树。下图是线段树的样子

两者还是有很多地方是不一样的。

lowbit函数

要想学会树状数组,我们必须要理解一个函数:lowbit()

lowbit()就是求一个数与上他的相反数,简单来说,就是x&(-x)

想要算lowbit(x)很简单,举个栗子:

假设一个数 x=5,5的二进制编码为0101。根据我们的二进制原理,则-x也就是1011,所以x&(-x)=1,如果你再举几个例子,细心地你就会发现,x的二进制编码的第一个1的位置就是lowbit(x)的值?

下面给出lowbit的函数模板

//version 1 
int lowbit(int x){
	return x&(-x);
}
//version 2
#define lowbit(x) x&(-x) 

结构分析

先放一张图片

        看了这一张图片,我们会发现,每一层的下表的二进制编码末尾的0的个数都是相同的,而且是从下至上一次递增的。

        那么原数组前4项的和t[4]=t[2]+t[3]+a[4]=t[1]+a[2]+t[3]+a[4]=a[1]+a[2]+a[3]+a[4]

        看了这一张图片之后,你是不是发现了更多的奥秘了呢? 树状数组中节点x的父节点为x+lowbit(x),例如t[2]的父节点为t[4]=t[2+lowbit(2)].

当你理解了这些之后,就可以尝试一下编写代码了.

单点修改,区间查询

        我们消化了上面的内容之后,会发现,编写树状数组的代码会变得尤为简单.在这里,我们先来看一下如何在树状数组里进行单点修改,区间查询.

        如果我们将a数组进行+k操作,那么它的父亲节点都要加上一个k. 举个栗子:如果我们要将a[1]数组进行+k操作,那么如上图t[1],t[2],t[4],t[8]都要加上一个k(因为我们是用差分建的数,所以更改单点的值时也要牵扯到它的祖宗).

        此时,我们就要运用到我们前面学的lowbit函数了!下面是单点修改的代码

int one_change(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i)){
        t[i]+=k;
    }
}

扯完了单点修改,我们继续来扯一下区间查询,区间查询又是什么呢,看一张图片,估计你们就明白了.

在这里插入图片描述

        图上求的是前7项的和ask[7]. 从图上也不难看出,ans[7]=t[7]+t[6]+t[4].如果你在多找几个例子,你会发现,所求前缀和的下标一次减去一个lowbit.所以,在代码中,我们可以进行使用lowbit来去求前缀和.接下来就是区间求和的代码

int find(int x){
	int sum=0;
	for(int i=x;i>=1;i-=lowbit(i)){
		sum+=t[i];
	}
	return sum;
}

        这时,喜欢刨根问底的你们就会给出一个很好地问题:上面这段代码只能实现[1,x]的区间求和,那么我想求[l,r]区间的和怎么办呢?此时就要用到前缀和的相减思想了(如果你们还没学会前缀和,那就停下这篇博客,赶快去学习前缀和吧).以下是更新后的代码.

int lrfind(int l,int r){
	int sum=0;
	for(int i=l;i>=1;i-=lowbit(i)){
        sum+=t[i];
    }
	for(int i=r-1;i>=1;i-=lowbit(i)){
        sum-=t[i];
    }
	return 0;
}

区间修改,单点查询

        我们讲完了单点修改,区间查询,现在就来讲解一下区间修改,单点查询.

        对于这一类操作,我们需要构造出原数组的差分数组,然后用树状数组维护该数组就行了

        如果要进行区间修改的话,我们只需要对差分数组进行操作就行了,例如对[l,r]区间进行+k操作,那么我们只需要更新差分数组change(l,k),change(r+1,-k)就行了,这是差分数组的性质.听懂了吗?下面是区间修改的代码

void lrchange(int pos,int k){
	for(int i=pos;i<=n;i+=lowbit(i)){
        t[i]+=k;
    }
}
lrchange(l,k);
lrchange(r+1,-k);

        如果进行单点查询操作,只需要求b数组的前缀和就行了,别忘了我们是根据差分建的树.下面是单点查询操作的代码

int one_find(int pos){
	int ans=0;
	for(int i=pos;i;i-=lowbit(i)){
        ans+=t[i];
    }
	return ans;
} 

区间修改,区间查询

若此模板使用树状数组过于复杂,不如使用高效的线段树进行解题.

模板题

树状数组1–单点修改,区间查询

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  • 将某一个数加上 x

  • 求出某区间每一个数的和

输入格式 

第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。

第二行包含n个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:

  • 1 x k 含义:将第 x个数加上k

  • 2 x y 含义:输出区间[x,y]内每个数的和

输出格式

输出包含若干行整数,即为所有操作 2 的结果。

输入输出样例

输入 #1

5 5
1 5 4 2 3
1 1 3
2 2 5
1 3 -1
1 4 2
2 1 4

输出 #1

14
16

说明/提示

【数据范围】

对于 30%30% 的数据,1≤n≤8,1≤m≤10;
对于 70%70% 的数据,1≤n,m≤104;
对于 100%100% 的数据,1≤n,m≤5×105。

数据保证对于任意时刻,a 的任意子区间(包括长度为 1 和 n 的子区间和均在 [−231,231) 范围内。

样例说明:

故输出结果14、16

分析

此题就是模板题,直接套模板上去就行了

代码

#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using ll=long long; 
using namespace std;
int c[2000006],n,m,x;
long long ans;
int one_change(int x,int k){
	for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=k;
    }
}
void lrfind(int l,int r){
	for(int i=r;i>=1;i-=lowbit(i)){
        ans+=c[i];
    }
	for(int i=l-1;i>=1;i-=lowbit(i)){
        ans-=c[i];
    }
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>x;
		one_change(i,x);
	}
	for(int i=1;i<=m;i++){
		int p,xx,yy;
		cin>>p>>xx>>yy;
		if(p==1){
            one_change(xx,yy);
        }else{
			ans=0;
			find(x,y);
			cout<<ans<<endl;
		}
	}
	return 0;
}

树状数组2––区间修改,单点查询

题目描述

如题,已知一个数列,你需要进行下面两种操作:

  1. 将某区间每一个数加上 x;

  2. 求出某一个数的值。

输入格式

第一行包含两个整数 N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含 N 个用空格分隔的整数,其中第 i个数字表示数列第 i 项的初始值。

接下来 M 行每行包含 2 或 4个整数,表示一个操作,具体如下:

操作 1: 格式:1 x y k 含义:将区间 [x,y] 内每个数加上k;

操作 2: 格式:2 x 含义:输出第 x 个数的值。

输出格式

输出包含若干行整数,即为所有操作 2的结果。

输入输出样例

输入 #1

5 5
1 5 4 2 3
1 2 4 2
2 3
1 1 5 -1
1 3 5 7
2 4

输出 #1

6
10

说明/提示

样例 1 解释:

故输出结果为 6、10。


数据规模与约定

对于 30% 的数据:N≤8,M≤10;

对于 70%的数据:N≤10000,M≤10000;

对于 100%100% 的数据:1≤N,M≤500000,1≤x,y≤n,保证任意时刻序列中任意元素的绝对值都不大于 230。

分析

同样是模板题,没有什么可以分析的,直接把代码套上去就行了

代码

#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&(-x)
using ll=long long;
long long n,q,f,a[1000005],l,r,x,p,b[1000005];
long long c[1000005];
int main(){
	cin>>n>>q;   
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=a[i]-a[i-1];
		for(int j=i;j<=n;j+=lowbit(j)){
			c[j]+=b[i];
		}
	}
	for(int i=1;i<=q;i++){
		cin>>f;
		if(f==1){
			cin>>l>>r>>x;
			for(int j=l;j<=n;j+=lowbit(j)){
				c[j]+=x;
			}
			for(int j=r+1;j<=n;j+=lowbit(j)){
				c[j]-=x;
			}
		}else{
			cin>>p;
			long long ans=0;
			for(int j=p;j>=1;j-=lowbit(j)){
				ans+=c[j];
			}
			cout<<ans<<endl;
		}
	}
	return 0;
}

最强编译器推荐

 到了最后,我给大家伙们推荐一个超好用的编译器(无论在什么系统上都可以)

https://lightly.teamcode.com/

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

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

相关文章

移动机器人路径优化:基于Q-learning算法的移动机器人路径优化(提供MATLAB代码)

一、Q-learning算法 Q-learning算法是强化学习算法中的一种&#xff0c;该算法主要包含&#xff1a;Agent、状态、动作、环境、回报和惩罚。Q-learning算法通过机器人与环境不断地交换信息&#xff0c;来实现自我学习。Q-learning算法中的Q表是机器人与环境交互后的结果&#…

哈工大计算机网络课程网络层协议详解之:CIDR与路由聚集

哈工大计算机网络课程网络层协议详解之&#xff1a;CIDR与路由聚集 文章目录 哈工大计算机网络课程网络层协议详解之&#xff1a;CIDR与路由聚集CIDR与路由聚集CIDR路由聚集 CIDR与路由聚集 CIDR CIDR&#xff1a;无类域间路由&#xff08;CIDR&#xff1a;Classless InterDo…

2.4C++派生类的函数

C 派生类的构造函数 在C中派生类的构造函数&#xff0c;必须调用基类的构造函数&#xff0c;来初始化从基类继承的数据成员。 具体有两种形式&#xff1a; 1、默认构造函数 2、带参数的构造函数 上面的我写的代码中&#xff0c;DerivedClass 构造函数的初始化列表中调用了 …

前缀和以及map混用,打开思路

补一个坑 目录 以力扣560为例&#xff1a; 力扣1248&#xff0c;优美子数组 力扣974 和可被 K 整除的子数组 力扣523.连续的子数组和 浅谈一下前缀和&#xff1a; 我们通过前缀和数组保存前 n 位的和&#xff0c;presum[1]保存的就是 nums 数组中前 1 位的和&#xff0c;也…

王道考研数据结构--3.双链表

目录 1.前言 2.代码难点 2.1双链表的插入和删除 3.代码函数 3.1双链表结构体定义 3.2双链表初始化函数 3.3双链表插入 3.4双链表节点删除 3.5双链表的遍历 4.全部代码 1.前言 日期&#xff1a;2023.6.21 书籍&#xff1a;2024年数据结构考研复习指导&#xff08;王道…

【计算机视觉】CVPR 23 | 视觉 Transformer 全新学习范式!用长尾数据提升ViT性能

文章目录 一、导读二、介绍三、方法四、总结 一、导读 论文地址&#xff1a; https://arxiv.org/abs/2212.02015代码链接&#xff1a; https://github.com/XuZhengzhuo/LiVT二、介绍 在机器学习领域中&#xff0c;学习不平衡的标注数据一直是一个常见而具有挑战性的任务。近…

JDK自带的构建线程池的方式之newScheduleThreadPool

顾名思义newScheduleThreadPool是一个定时任务的线程池&#xff0c;这个线程池可以定时一定周期去执行任务&#xff0c;也可以实现延迟多久去执行任务一次。 newScheduleThreadPool方法实现展示 基于查看这个方法不难发现&#xff0c;该方法是基于ScheduledThreadPoolExecutor…

基于 Flink CDC 构建 MySQL 到 Databend 的 实时数据同步

这篇教程将展示如何基于 Flink CDC 快速构建 MySQL 到 Databend 的实时数据同步。本教程的演示都将在 Flink SQL CLI 中进行&#xff0c;只涉及 SQL&#xff0c;无需一行 Java/Scala 代码&#xff0c;也无需安装 IDE。 假设我们有电子商务业务&#xff0c;商品的数据存储在 My…

【深度学习】5-5 与学习相关的技巧 - 超参数的验证

超参数指的是&#xff0c;比如各层的神经元数量、batch大小、参数更新时的学习率或权值衰减等。如果这些超参数没有设置合适的值&#xff0c;模型的性能就会很差。 那么如何能够高效地寻找超参数的值的方法 验证数据 之前我们使用的数据集分成了训练数据和测试数据&#xff0c…

WorkPlus AI助理正式上线!为企业打造定制化的AI私有助理

毋庸置疑&#xff0c;ChatGPT的应用充满无限的想象空间。但对于企业来说&#xff0c;使用时面临的最核心的问题就是“存在回答准确性不足”的弊端。那企业都想要通过GPT构建内容生态&#xff0c;在数字化时代保持行业领先地位。 企业都想要结合行业属性、业务需求等自身特点打…

【Flutter】Flutter 数据存储 Hive 的简要使用说明

文章目录 一、前言二、Hive 包的版本号三、Hive 简介1. Hive 是什么&#xff1f;2. Hive 的特点 四、Hive 的基本使用1. Hive 的安装2. Hive 的初始化3. 创建和打开 Hive 数据库4. 数据的存储和读取5. 数据的删除 五、总结 一、前言 &#x1f389;想要精通 Flutter&#xff0c…

是时候扔掉cmder, 换上Windows Terminal

作为一个Windows的长期用户&#xff0c;一直没有给款好用的终端&#xff0c;知道遇到了 cmder&#xff0c;它拯救一个习惯用Windows敲shell命令的人。 不用跟我安利macOS真香&#xff01;公司上班一直用macOS&#xff0c;一方面确实更加习惯windows下面学习, 另一方面是上课需要…

Phantomjs实现后端将URL转换为图片

PhantomJS简介 PhantomJS is a command-line tool. – 其实就是一个命令行工具 PhantomJS的下载地址&#xff1a; Windows:phantomjs-2.1.1-windows.zip Linux:phantomjs-2.1.1-linux-x86_64.tar.bz2;phantomjs-2.1.1-linux-i686.tar.bz2 MacOS:phantomjs-2.1.1-macosx.zip…

西门子Mendix 入门 2

今天还是一直下载失败&#xff0c;就算成功了&#xff0c;速度也只有几K&#xff0c;于是使用翻墙软件&#xff0c;最终下载成功 下载成功后重新点击edit in studio pro 出现如下页面 首先先关闭安全性 进行添加任务和管理任务 点击上方绿色箭头后点击View App 出现如下页面…

ESP32-WROOM-32 UDP单播透传AT指令例程

ESP32-WROOM-32 AT指令配置TCP通讯 ESP32-WROOM-32前言固件烧录测试AT指令UDP单播通讯\透传ESP32配置SoftAPESP32与手机间的UDP通讯与透传普通传输模式演示UDP透传演示 ESP32-WROOM-32 前言 上次演示了ESP32与手机的三种TCP连接与数据传输方法&#xff0c;现在接着上一篇“ESP…

第二章 数据结构(一)——链表,栈和队列与kmp

文章目录 链表栈和队列表达式运算 单调栈单调队列kmp链表练习题826. 单链表827. 双链表 栈和队列练习题828. 模拟栈3302. 表达式求值829. 模拟队列830. 单调栈154. 滑动窗口 kmp练习题831. KMP字符串 kmp虐我一下午 链表 若用链式结构实现链表&#xff0c;效率低&#xff0c;因…

软件开发流程

目录 软件软件开发流程的演变 瀑布模型敏捷模型 XPSCRUMDevOps 1.软件 与计算机系统操作有关的计算机程序、可能有的文件、文档及数据。 软件可以分为两种主要类型&#xff1a; 独立软件&#xff1a;独立软件是一种完整的应用程序&#xff0c;可以直接在计算机或移动设备上…

Android系统安全 — 6.2 Ethernet安卓架构

1. Android Ethernet架构介绍 整个Ethernet系统架构如下图所示&#xff1a; 以太网服务&#xff08;EthernetService&#xff09;的启动与注册流程&#xff1b;应用层调用使能ethernet功能的方法流程来分析&#xff0c;从应用层如何将指令一步一步传到底层kernel&#xff1b;…

SAAS-HRM系统概述与搭建环境

SAAS-HRM系统概述与搭建环境 学习目标&#xff1a; 理解SaaS的基本概念 了解SAAS-HRM的基本需求和开发方式掌握Power Designer的用例图 完成SAAS-HRM父模块及公共模块的环境搭建完成企业微服务中企业CRUD功能 初识SaaS 云服务的三种模式 IaaS&#xff08;基础设施即服务…

使用Windows To Go工具制作你的U盘系统【含下载Windows10系统镜像】亲测已成功23.06.21

WinToGo是一款辅助工具&#xff1a;专为能够让你将系统装进U盘&#xff0c;移动硬盘里&#xff0c;让你在任意电脑都能运行U盘里装的系统&#xff01; 一、下载&#xff0c;安装“Windows To Go”工具 1、下载Windows To Go工具 口袋系统WinToGo: 安装Win 10到U盘 2、双击Wi…