【C++】树状数组的使用、原理、封装类、样例

【C++】树状数组的使用、原理、封装类、样例

前言

本博文代码打包下载
C++算法与数据结构分类汇总

树状数组的英文名称是 ‌Binary Indexed Tree‌,缩写为 ‌BIT‌。我习惯翻译成TreeArr。

最常见的应用

有序集合包括若干整数,求小于x的数量。auto it = s.lower(x) , it - s.begin(),这个时间复杂度是O(n)。
由于查询和插入交替进行,故不能用向量。

树状数组的用途

令原始数组是a,长度为n。

基础操作

一,求前缀和。即 ∑ j : 0 i a [ j ] \sum_{j:0}^ia[j] ∑j:0i​a[j]。时间复杂度:O(logn)。
二,a[i] +=x。时间复杂度:O(logn)

组合操作

区间和:两个前缀和相减便是区间和,a[i…j]之和=a[0…j]之和-a[0…i-1]之和。
求a[i]的值:即区间a[i…i]之和。
a[i]=x: y=a[i]的值,a[i] += (x-y)

树状数组的原理

性质一,下标从1开始。
性质二,f(x)= x&(x-1) ,即将最低位的1变成0,f(6)=4。data[i]=a[f(i) + 1] 到a[i]之和。故a[1…i] 之和等于按以下方式迭代的a[i]之和:i = f(i)。
性质三,g(i)=x&(-x)即最低位1的值,f(6)=2。a[i]+= x,等于data[i]+=x,按如下方式迭代i: i +g(x)。
性质四:f(i)最低位1后面的任意一个0变成1,一定是对应i。且不存在其它方式产生对应的i。
性质五:f(j) < i <= j, f(j)中1的个数不会多于f(i)。如果f(j)小于f(i),根据性质四无法让j >=i。令f(j)的第k3位小于f(i),如果有多个k3,取最高位。不修改k3或更高位,无法大于等于i。
如果f(j)>f(i),说明i的最低位1后面有1,根据性质四,无法如何j都不会大于i。
令i的最后一个1是第k4位,只讨论比k4高的位,f(j)<=i,否则整个f(j)>i。比k4高的位f(i)等于i,故f(j)和f(i)k4为之前部分相等。f(j)>f(i),故必须比k4低的位有1。
性质六:如果f(j)有k个1,这k个1一定是f(i)的前k个1。证明同性质五。
如果f(j)有k个1,i从高位到低位第k个1是k1位,第k+1个1是k2位。 如果i有k+1个1.则j最后一个1为[k2,k1 - 1];否则j最后一个1为[k2 + 1, k1 - 1]。证明结束。

树状数组的封装类

静态开点求和和求异或和的树状数组。

template<classELE=int>classCTreeArrAddOpe:publicITreeArrSumOpe<ELE>{public:virtualvoidAssign(ELE& dest,const ELE& src){ dest += src;}virtual ELE Back(const ELE& n1,const ELE& n2){return n1 - n2;}};template<classELE=int,classELEOpe= CTreeArrAddOpe<ELE>>classCTreeArr{public:CTreeArr(int iSize):m_vData(iSize +1){}voidAdd(int index, ELE value){if((index <0)||(index >= m_vData.size()-1)){return;} index++;while(index < m_vData.size()){ m_ope.Assign(m_vData[index], value); index += index &(-index);}} ELE Sum(int index)//[0...index]之和{ index++; ELE ret =0;while(index){ m_ope.Assign(ret, m_vData[index]); index -= index &(-index);}return ret;} ELE Sum(){returnSum(m_vData.size()-2);} ELE Get(int index){return m_ope.Back(Sum(index),Sum(index -1));}private: ELEOpe m_ope; vector<ELE> m_vData;};template<classELE=int>classCTreeArrXorOpe:publicITreeArrSumOpe<ELE>{public:virtualvoidAssign(ELE& dest,const ELE& src){ dest ^= src;}virtual ELE Back(const ELE& n1,const ELE& n2){return n1 ^ n2;}};template<classELE=int>classCTreeArrXor:publicCTreeArr<ELE,CTreeArrXorOpe<ELE>>{public:using CTreeArr<ELE, CTreeArrXorOpe<ELE>>::CTreeArr;};

动态开点树状数组

template<classELE=int,classELEOpe= CTreeArrAddOpe<ELE>>classCTreeArrMap{public:CTreeArrMap(longlong llMin,longlong llMax):m_llMin(llMin),m_llMax(llMax){}voidAdd(longlong index, ELE value){if((index < m_llMin)||(index > m_llMax)){return;} index = index - m_llMin +1;auto maxIndex = m_llMax - m_llMin +1;while(index <= maxIndex){ m_ope.Assign(m_vData[index], value); index += index &(-index);}} ELE Sum(longlong index)//[0...index]之和{if((index < m_llMin)||(index > m_llMax)){return0;} index = index - m_llMin +1; ELE ret =0;while(index){ m_ope.Assign(ret, m_vData[index]); index -= index &(-index);}return ret;} ELE Sum(){returnSum(m_llMax - m_llMin +1);} ELE Get(longlong index){return m_ope.Back(Sum(index),Sum(index -1));}private: ELEOpe m_ope; unordered_map <longlong, ELE> m_vData;constlonglong m_llMin, m_llMax;};

最值树状数组

template<classT=int,T def = INT_MIN>classCTreeArrMax{public:CTreeArrMax(int iEleSize):m_iMax(iEleSize){ m_aMax.assign(iEleSize +1, def); m_aRangMax.assign(iEleSize +1, def);}voidModify(int indexBase0, T value){ indexBase0++;if(value <= m_aMax[indexBase0]){return;} m_aMax[indexBase0]= value;while(indexBase0 <= m_iMax){ m_aRangMax[indexBase0]=max(m_aRangMax[indexBase0], value); indexBase0 +=BitLower(indexBase0);}} T Query(int leftBas0,int rBase0){ leftBas0++; rBase0++; leftBas0 =max(1, leftBas0); rBase0 =min(m_iMax, rBase0); T iMax = def;while(rBase0 >= leftBas0){constint pre = rBase0 -BitLower(rBase0);if(pre +1>= leftBas0){ iMax =max(iMax, m_aRangMax[rBase0]); rBase0 = pre;}else{ iMax =max(iMax, m_aMax[rBase0]); rBase0--;}}return iMax;}protected:intBitLower(int x){return x &(-x);}constint m_iMax; vector<T> m_aMax, m_aRangMax;};template<classT=int, T def = INT_MAX>classCTreeArrMin{public:CTreeArrMin(int iEleSize):m_max(iEleSize){}voidModify(int indexBase0, T value){ m_max.Modify(indexBase0,-value);} T Query(int leftBas0,int rBase0){return-m_max.Query(leftBas0, rBase0);} CTreeArrMax<T,-def> m_max;};typedef CTreeArrMin<longlong, LLONG_MAX> CTreeArrLLMin;

和差分数组结合

可以在O(logn)的时间内进行区间修改,在O(logn)的时间内进行单点查询。

树状数组使用样例

力扣

难度分
【C++二分查找 树状数组】2424. 最长上传前缀1604
【C++ 树状数组】2426. 满足不等式的数对数目2030
【C++ 树状数组 】1850. 邻位交换的最小次数2073
【字符串】【贪心】【 树状数组】2193. 得到回文串的最少操作次数2090
【C++树状数组】3187. 数组中的峰值2154
【树状数组】1649. 通过指令创建有序数组2207
【树状数组 一一映射】2179. 统计数组中好三元组数目2272
【状态机dp 线段树 树状数组】2407. 最长递增子序列 II2280
【树状数组】2659. 将数组清空2281
【树状数组 队列】1505. 最多 K 次交换相邻数位后得到的最小整数2336

洛谷

难度等级
【树状数组 C++二分查找】P7333 [JRKSJ R1] JFCA普及+
【前缀和 前后缀分解 树状数组】P9744 「KDOI-06-S」消除序列普及+
【C++贪心 树状数组】P8818 [CSP-S 2022] 策略游戏普及+
【树状数组 二分】P10235 [yLCPC2024] C. 舞萌基本练习普及+
【树状数组】P5367康托展开普及+
【树状数组 贪心】P11453 [USACO24DEC] Deforestation S普及+
【树状数组】P10673 【MX-S1-T2】催化剂普及+
【树状数组 排序 双指针】P11048 [蓝桥杯 2024 省 Java B] 拼十字普及+
【树状数组】P6278 [USACO20OPEN] Haircut G普及+
【树状数组】P3608 [USACO17JAN] Balanced Photo G普及+
【树状数组】P5200 [USACO19JAN] Sleepy Cow Sorting G普及+
【树状数组 位运算】P6225 [eJOI 2019] 异或橙子普及+
【树状数组】P5459 [BJOI2016] 回转寿司普及+
【树状数组】P5094 [USACO04OPEN] MooFest G 加强版普及+
【差分数组 树状数组】P5057 [CQOI2006] 简单题普及+
【树状数组 】P7629 [COCI 2011/2012 #1] SORT普及+

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步ZEEKLOG学院,听白银讲师(也就是鄙人)的讲解。
https://edu.ZEEKLOG.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.ZEEKLOG.net/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

Read more

Flutter 组件 serverpod_swagger 的鸿蒙化适配实战 - 自动化生成后端映射、Swagger UI 桥接与 API 交互效率提升方案

Flutter 组件 serverpod_swagger 的鸿蒙化适配实战 - 自动化生成后端映射、Swagger UI 桥接与 API 交互效率提升方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 serverpod_swagger 的鸿蒙化适配实战 - 自动化生成后端映射、Swagger UI 桥接与 API 交互效率提升方案 前言 在现代的全栈 Flutter 开发架构中,Serverpod 以其“代码即协议”的理念,打破了前后端通信的繁冗壁垒。然而,当后端模型不断膨胀,如何让前端(尤其是正在飞速扩张的鸿蒙端)开发者能够直观地查看、调试并自动生成对应的 API 调用代码? serverpod_swagger 应运而生。它是 Serverpod 生态中负责生成符合 OpenAPI 标准(Swagger)协议的核心模块,能够将复杂的后端 Model 和 Endpoint 瞬间转化为标准的 Swagger

By Ne0inhk
[特殊字符]颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发论文检索MCP Server!

[特殊字符]颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发论文检索MCP Server!

🔥🔥🔥本篇笔记所对应的视频:🚀颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发MCP服务_哔哩哔哩_bilibili Open WebUI 的 MCPo 项目:将 MCP 工具无缝集成到 OpenAPI 的创新解决方案 随着人工智能工具和模型的快速发展,如何高效、安全地将这些工具集成到标准化的 API 接口中成为了开发者面临的重要挑战。Open WebUI 的 MCPo 项目(Model Context Protocol-to-OpenAPI Proxy Server)正是为了解决这一问题而设计的。本文将带您深入了解 MCPo 的功能、优势及其对开发者生态的影响。 什么是 MCPo? MCPo 是一个简单、可靠的代理服务器,能够将任何基于 MCP 协议的工具转换为兼容

By Ne0inhk
Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一)

Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一)

系列文章目录 一、Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一) 二、Qwen3+Qwen Agent +MCP智能体开发实战(二)—10分钟打造"MiniManus" 前言 要说最近人工智能界最火热的开源大模型,必定是阿里发布不久的Qwen3系列模型。Qwen3模型凭借赶超DeepSeek-V3/R1的优异性能,创新的混合推理模式,以及极强的MCP能力迅速成为AI Agent开发的主流基座模型。大家可参考我的文章一文解析Qwen3大模型详细了解Qwen3模型的核心能力。有读者私信我: “Qwen3官网特地强调增强了Agent和代码能力,同时加强了对MCP的支持,那么我该如何利用Qwen3快速开发MCP应用呢?” 这就就需要使用我们今天的主角——Qwen官方推荐的开发工具Qwen-Agent ,本期分享我们就一起学习快速使用Qwen3+QwenAgent 接入MCP服务端,快速开发AI Agent应用! 一、注册 Qwen3 API-Key 本次分享通过阿里云百炼大模型服务平台API Key请求方式调用Qwen3大模型,获取服务平台

By Ne0inhk