c++树状数组(超多例题讲解)适合有相应基础的

树状数组(Fenwick Tree)是一种用于高效计算前缀和的数据结构,具有较小的内存占用和较快的查询、更新操作。它广泛应用于解决一维数组的区间查询问题。

树状数组的原理基于二进制的思想。假设有一个长度为n的数组A,树状数组就是用一个长度为n的辅助数组C来模拟A数组的前缀和。数组C的索引i表示原数组A的前i个元素的和,数组C的值表示A数组对应前缀的和。

树状数组的核心操作有两个:区间和查询和单点更新。

区间和查询:给定一个区间[l, r],要求计算出原数组A[l, r]的和。使用树状数组的查询操作如下:
1. 初始化一个变量sum为0。
2. 从r开始,将r的最低位的1置为0(即r = r - (r & -r)),并将sum加上C[r]的值。
3. 重复步骤2,直到r为0。
4. 从l开始,将l的最低位的1置为0(即l = l - (l & -l)),并将sum减去C[l]的值。
5. 重复步骤4,直到l为0。
6. 返回sum。

单点更新:给定一个索引i和一个增量delta,要求将原数组A[i]的值加上delta。使用树状数组的更新操作如下:
1. 从i开始,将i的最低位的1加上delta(即i = i + (i & -i))。
2. 重复步骤1,直到i大于数组长度n。

通过这两个核心操作,可以高效地实现对原数组的区间查询和单点更新。

需要注意的是,树状数组的索引从1开始,因此在使用时需要对原始数据进行适当的处理。同时,树状数组只能处理非负数据,对于负数的处理需要进行适当的转换或者使用其他数据结构。

例题1:求每个数在数组中的逆序数和总逆序数

给定一个 1∼N 的随机排列,要求一次只能交换相邻两个数,那么最少需要交换多少次才可以使数列按照从小到大排列呢?

请你求出一个待排序序列的最少交换次数和对应的逆序数列

输入格式

第一行一个整数 N。

第二行一个 1∼N的排列。

输出格式

第一行输出逆序数列,数之间用空格隔开。

第二行输出最少交换次数。

数据范围

1≤N≤1000

输入样例:

8
4 8 2 7 5 6 1 3

输出样例:

6 2 5 0 2 2 1 0
18

代码:

#include<bits/stdc++.h>using namespace std;typedef long long LL;
const int N = 1e5 + 10;int n;
int a[N], tr[N];
int f[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 ask(int x)
{LL 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];f[y] = ask(n) - ask(y);add(y, 1);}int res = 0;for(int i = 1; i <= n; i ++) {cout << f[i] << " ";res += f[i];}cout << endl << res;return 0;
}

1、f[N]数组存储的是所有在i前面,比a[i]小的数据

2、样例解释:,第一个的逆序对为什么为6,因为求的是就是数值为1的逆序对数量,在1前面有6个数比1大,所以逆序对为6

3、

例题2:逆序对的扩展

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

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

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

西部 314314 认为这幅壁画所包含的信息与这 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

代码

#include<bits/stdc++.h>using namespace std;typedef long long LL;
const int N = 2e5 + 10;int n;
int a[N], tr[N];
int f[N];
int g[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;
}LL ask(int x)
{LL 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]);for(int i = 1; i <= n; i ++){int y = a[i];f[i] = ask(y - 1);g[i] = ask(n) - ask(y);add(y, 1);}memset(tr, 0, sizeof tr);LL resV = 0, resA = 0;for(int i = n; i; i --){int y = a[i];resV += (LL)g[i] * (ask(n) - ask(y));resA += (LL)f[i] * ask(y - 1);add(y, 1);}cout << resV << " " << resA << endl;return 0;
}

1、求所以V 的个数和  的个数,而且严格y1∼yn是 1到 n 的一个排列,所以可以不用离散化处理,求v的个数,所以求出每一个数,左边比它的大的和右边都比它大,然后相乘在全部相加既可,求 的个数也是如此的思路

2、从左到右扫描一遍,f[i]存储的是当前比a[i]小的集合,g[i]存储的是当前比a[i]大的集合,然后建树

3、重新初始化,然后从从右到左扫描一遍

4、树状数组求逆序对,让我们知道了如何在一个序列中计算每个数后面有多少个数比它小,因此我们可以通过这个性质来做一些事情
‘v’图腾求法
倒序扫描序列a,利用树状数组求出每个a[i]后面有几个数比它大记录为g[i]
正序扫描序列a,利用树状数组求出每个a[i]前面有几个数比它大,记录为r[i]
’^’图腾求法
倒序扫描序列a,利用树状数组求出每个a[i]后面有几个数比它小,记录为g[i]
正序扫描序列a,利用树状数组求出每个a[i]前面有几个数比它小,记录为f[i]

例题三:区间和和单点修改

给定 n个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b]的连续和。

输入格式

第一行包含两个整数 n和 m,分别表示数的个数和操作次数。

第二行包含 n 个整数,表示完整数列。

接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。

数列从 1 开始计数。

输出格式

输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。

数据范围

1≤n≤100000,
1≤m≤100000,
1≤a≤b≤n,
数据保证在任何时候,数列中所有元素之和均在 int 范围内。

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
1 1 5
0 1 3
0 4 8
1 7 5
0 4 8

输出样例:

11
30
35

代码:

#include<bits/stdc++.h>using namespace std;const int N = 1e5 + 10;int n, m;
int a[N], tree[N];int lowbit(int x)
{return x & -x;
}void add(int x, int c)
{for(int i = x; i <= n; i += lowbit(i)) tree[i] +=c;
}int ask(int x)
{int res = 0;for(int i = x; i; i-= lowbit(i)) res += tree[i];return res;
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);for(int i = 1; i <= n; i ++) add(i, a[i]);while(m --){int k, x, y;scanf("%d%d%d", &k, &x, &y);if(k == 0){cout << ask(y) - ask(x - 1) << endl;}else add(x, y);}return 0;
}

解析:1、因为是单点修改,所以正常建树,把每个数据压入就行

2、求区间和,从r到l - 1求的就是区间l - r 的和

样例四:区间修改和单点查询

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

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

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

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

输入格式

第一行包含两个整数 N 和 M。

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

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

输出格式

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

每个答案占一行。

数据范围

1≤N,M≤10^5,
|d|≤10000,
|A[i]|≤10^9

输入样例:
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

代码:

#include<bits/stdc++.h>using namespace std;typedef long long LL;
const int N = 1e5 + 10;int n, m;
int a[N], 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 ask(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 ++) scanf("%d", &a[i]);for(int i = 1; i <= n; i ++) add(i, a[i] - a[i - 1]);while(m --){char op[2];scanf("%s", op);if(*op == 'C'){int l, r, d;scanf("%d%d%d", &l, &r, &d);add(l, d), add(r + 1, -d);}else{int x;cin >> x;cout << ask(x) << endl;}}return 0;
}

1、因为是区间修改,所以我们可以使用差分来降低时间复杂度

2、区间修改,所以我们是两个端点修改就可以了

样例五:区间查询和区间修改

既可以树状数组,也可以使用线段树,但是这里就使用树状数组来做

给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 A[l],A[l+1],…,A[r] 都加上 d。
  2. Q l r,表示询问数列中第 l∼r个数的和。

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

输入格式

第一行两个整数 N,M。

第二行 N 个整数 A[i]。

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

输出格式

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

每个答案占一行。

数据范围

1≤N,M≤10^5,
|d|≤10000,
|A[i]|≤10^9

输入样例:
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

代码:

#include<bits/stdc++.h>using namespace std;typedef long long LL;
const int N = 1e5 + 10;int n, m;
int a[N];
LL tr1[N], tr2[N];int lowbit(int x)
{return x & -x;
}void add(LL tr[], LL x, LL c)
{for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}LL ask(LL tr[], LL x)
{LL res = 0;for(int i = x; i; i -= lowbit(i)) res += tr[i];return res;
}LL get_sum(LL x)
{return ask(tr1, x) * (x + 1) - ask(tr2, x);
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);for(int i = 1; i <= n; i ++){LL b = a[i] - a[i - 1];add(tr1, i, b);add(tr2, i, b * i);}while(m --){char op[2];scanf("%s", op);if(*op == 'Q'){int l, r;scanf("%d%d", &l, &r);cout << get_sum(r) - get_sum(l - 1) << endl;;}else{int l, r, d;scanf("%d%d%d", &l, &r, &d);add(tr1, l, d), add(tr2, l, l * d);add(tr1, r + 1, -d), add(tr2, r + 1, (r + 1) * -d);}}return 0;
}

1、因为是区间修改,所以还是使用差分来做,这里维护两个树状数组,tr1存储的是修改区间的数组,tr2存储的是区间的数值

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

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

相关文章

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用

基于STC12C5A60S2系列1T 8051单片机的TM1638键盘数码管模块的按键扫描、数码管显示按键值、显示按键LED应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式及配置STC12C5A60S2系列1T 8051单片机I/O口各种不同工作模式介绍TM1638键盘…

C# WinForm AndtUI第三方库 Tree控件使用记录

环境搭建 1.在NuGet中搜索AndtUI并下载至C# .NetFramework WinForm项目。 2.添加Tree控件至窗体。 使用方法集合 1.添加节点、子节点 using AntdUI; private void UpdateTreeView() {Tree tvwTestnew Tree();TreeItem rootTreeItem;TreeItem subTreeItem;Dictionary<str…

高级软件开发知识点

流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题&#xff0c;理解其思想&#xff0c;不要死记最近一家公司所负责的业务和项目&#xff1a; 项目背景、演进之路&#xff0c;有哪个阶段&#xff0c;每个阶段主要做什么项目中技术选型…

【战略前沿】人形机器人制造商Figure获得了OpenAI、Jeff Bezos、Nvidia和其他科技巨头的资助

原文&#xff1a;Humanoid robot-maker Figure gets funding from OpenAI, Jeff Bezos, Nvidia, and other tech giants 作者&#xff1a;ASSOCIATED PRESS ———————————————— Figure成立不到两年&#xff0c;还没有商业产品&#xff0c;但正在说服有影响力的…

STM32 TIM编码器接口

单片机学习&#xff01; 目录 文章目录 前言 一、编码器接口简介 1.1 编码器接口作用 1.2 编码器接口工作流程 1.3 编码器接口资源分布 1.4 编码器接口输入引脚 二、正交编码器 2.1 正交编码器功能 2.2 引脚作用 2.3 如何测量方向 2.4 正交信号优势 2.5 执行逻辑 三、编码器定时…

[DevOps云实践] 彻底删除AWS云资源

[DevOps云实践] 彻底删除AWS云资源 在实际项目当中&#xff0c;我们经常会遇到云设施资源的删除。如果资源删除不干净&#xff0c;时间一长便会被一些莫名其妙的费用所困扰。 本文将把实际项目过程当中&#xff0c;删除资源时候容易遗漏的点讲解给大家。 1. 彻底删除EC2实例 …

[AIGC] Flink中的Max和Reduce操作:区别及使用场景

Apache Flink提供了一系列的操作&#xff0c;用于对流数据进行处理和转换。在这篇文章中&#xff0c;我们将重点关注两种常见的操作&#xff1a;Max和Reduce。虽然这两种操作在表面上看起来类似——都是对数据进行一些形式的聚合&#xff0c;但它们在应用和行为上有一些关键的区…

15-Java责任链模式 ( Chain of Responsibility)

Java责任链模式 摘要实现范例 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;为请求创建了一个接收者对象的链 责任链模式给予请求的类型&#xff0c;对请求的发送者和接收者进行解耦 责任链模式中通常每个接收者都包含对另一个接收者的引用&#xff0c…

【replace跳转 和push跳转】

"跳转"一词通常用于指示在程序或网页中从一个位置或页面转到另一个位置或页面。 替代"replace跳转": "replace跳转"通常用于在浏览器中替换当前页面的历史记录。如果您想要替代这种行为&#xff0c;您可以考虑使用以下方法&#xff1a; 使用Ja…

头像剪切上传

头像剪切上传 文章说明核心Api示例源码效果展示源码下载 文章说明 本文主要为了学习头像裁剪功能&#xff0c;以及熟悉canvas绘图和转文件的相关操作&#xff0c;参考教程&#xff08;Web渡一前端–图片裁剪上传原理&#xff09; 核心Api 主要就一个在canvas绘图的操作 context…

2.8k star! 用开源免费的edge-tts平替科大讯飞的语音合成服务

edge-tts是github上的一个开源项目&#xff0c;可以免费将文本转为语音&#xff0c;别看它只有2.8k star&#xff0c;替代科大讯飞的收费TTS服务完全没问题&#xff0c;因为这个项目实际是调用的微软edge的在线语音合成服务&#xff0c;支持40多种语言&#xff0c;300多种声音&…

注意力机制(代码实现案例)

学习目标 了解什么是注意力计算规则以及常见的计算规则.了解什么是注意力机制及其作用.掌握注意力机制的实现步骤. 1 注意力机制介绍 1.1 注意力概念 我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的), 是因为我们大脑能够很快把注意力放在事物…

NLP_jieba和hanlp词性对照表_6

jieba词性对照表: - a 形容词 - ad 副形词 - ag 形容词性语素 - an 名形词 - b 区别词 - c 连词 - d 副词 - df - dg 副语素 - e 叹词 - f 方位词 - g 语素 - h 前接成分 - i 成语 - j 简称略称 - k 后接成分 - l 习用语 …

深入了解线程池(代码实战)

文章目录 前言一、线程池是什么&#xff1f;二、如何创建线程池1.使用Executors类2.使用ThreadPoolExecutor类手动配置线程池 总结 前言 随着计算机系统的不断发展和进步&#xff0c;我们需要处理更多的并发任务和复杂的操作。而线程池作为一种高效的线程管理机制&#xff0c;…

EdgeX Foundry 安装部署

文章目录 一、概述1.官方文档2.Docker Compose 生成器3.创建 docker-compose 文件 二、安装准备1. 克隆服务器2.安装 Docker3.安装 docker-compose 三、非安全模式部署1.docker-comepse2.启动 EdgeX Foundry3.访问 UI3.1. consul3.2. EdgeX Console EdgeX Foundry # EdgeX Fou…

Android之Handler原理解析与问题分享

一、Handler运行原理剖析 1.关系剖析图 如果把整个Handler交互看做一个工厂&#xff0c;Thread就是动力MessageQueue是履带Looper是转轴Loooper的loop方法就是开关&#xff0c;当调用loop方法时整个工厂开始循环工作&#xff0c;处理来自send和post提交到MessageQueue的消息&a…

SQL执行后台脚本

SQL进程中断实验 我们操作数据库时&#xff0c;经常遇到数据导入等特别耗时的SQL操作&#xff0c;而关闭MySQL客户端或SSH终端&#xff0c;就会立马关闭SQL会话&#xff0c;导致SQL执行中断&#xff0c;如下实验&#xff1a; 在第一个SSH终端执行 # 进入Mysql客户端&#xf…

08. Nginx进阶-Nginx动静分离

简介 什么是动静分离&#xff1f; 通过中间件将动态请求和静态请求进行分离。分离资源&#xff0c;减少不必要的请求消耗&#xff0c;减少请求延时。 动静分离的好处 动静分离以后&#xff0c;即使动态服务不可用&#xff0c;静态资源仍不受影响。 动静分离示意图 动静分离…

Day16:信息打点-语言框架开发组件FastJsonShiroLog4jSpringBoot等

目录 前置知识 指纹识别-本地工具-GotoScan&#xff08;CMSEEK&#xff09; Python-开发框架-Django&Flask PHP-开发框架-ThinkPHP&Laravel&Yii Java-框架组件-Fastjson&Shiro&Solr&Spring 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/…

Linux中断实验:定时器按键消抖处理实验一测试

一. 简介 前面文章实现了定时器对按键的消抖处理,文章地址如下: Linux中断实验:定时器实现按键消抖处理-CSDN博客 本文对所实现的定时器对按键消抖功能进行测试。确认定时器是否实现对按键消抖的功能。 二. Linux中断实验:定时器按键消抖处理的测试 1. 拷贝驱动模块…