【树状数组·进阶篇】树状数组实现平衡树(树状数组上二分)
余未曾想到,树状数组居然是一个这么高端的东西。
从未想到过树状数组上还可以二分,从未想到过树状数组还能实现平衡树的功能。
唔姆,树状数组果然也是美丽的,余喜欢一切美丽的东西。
树状数组上二分
众所周知,树状数组上的维护的是。
考虑强行令树状数组的大小为的幂。
每次二分边界,中点就是。
然后就会发现,因为树状数组大小是的幂,每次二分其实就相当于在判断二进制下某一位是否能填作。
因此,的值实际上就等于!
唔姆,假设树状数组的表示等于的数的个数,那么的值等同于值在范围内的数的个数。
于是就能轻松实现第大的询问了。
模板:普通平衡树
其实只要能够求第大,平衡树的其他操作都非常简单了,前驱/后继都可以转化为询问排名再询问第大的操作。
唯一一个特殊注意点,就是由于这道题中值可能为负,需要把每个数都加上再操作。
代码封装,码风毒瘤(尽管在这道题中体现不多)。
/*友情提醒:I表示inline,可忽略;RI表示register int,CI表示const int&,可以直接视作int;W表示while*/
class TreeArray//树状数组实现平衡树
{
private:
#define V (1<<25)//V存储总值域,必须为2的幂
#define P 10000000//因为存在负数,所有数据都要加上P
int a[V+5];I void U(RI x,CI v) {W(x<=V) a[x]+=v,x+=x&-x;}//后缀修改
I int Q(RI x,RI t=0) {W(x) t+=a[x],x-=x&-x;return t;}//求前缀和(小于等于x的数的个数)
public:
I int Kth(RI k) {RI l=1,r=V,mid;W(l^r) a[mid=l+r>>1]<k?(k-=a[mid],l=mid+1):r=mid;return l-P;}//树状数组上二分询问第k大
I void Add(CI x) {U(x+P,1);}I void Del(CI x) {U(x+P,-1);}I int Rk(CI x) {return Q(x+P-1)+1;}//插入;删除;询问排名
I int Pre(CI x) {return Kth(Q(x+P-1));}I int Nxt(CI x) {return Kth(Q(x+P)+1);}//前驱;后继
};
[省选联考 2020 A/B 卷] 冰火战士
- 有两个数组。
- 每次操作修改某个数组中某个位置上的数。(始终非负)
- 每次操作完求一个最大的整数,使得最大,并求出这个最大值。
- 操作数量
很显然,随增大递增,随增大递减。
那么根据初中函数知识就可以知道,要使最小值最大,就是求两个函数的交点。
题目很简单,可惜这道题居然卡线段树!
这时候就要请出树状数组上二分啦!
每次先二分出最大的满足,由于是整数因此不一定能找到交点,还要比较与谁更大。
求出最大值之后,还要再次树状数组上二分求出最大的。
大致思路差不多就这样啦。
树状数组最大的优点就是代码短、常数小,因此真的真的是个非常美丽的算法哦!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具