【树状数组·进阶篇】树状数组实现平衡树(树状数组上二分)

Preface

余未曾想到,树状数组居然是一个这么高端的东西。

从未想到过树状数组上还可以二分,从未想到过树状数组还能实现平衡树的功能。

唔姆,树状数组果然也是美丽的,余喜欢一切美丽的东西。

树状数组上二分

众所周知,树状数组上的ai维护的是k=ilowbit(i)+1ivalk

考虑强行令树状数组的大小为2的幂

每次二分边界[l,r],中点就是mid

然后就会发现,因为树状数组大小是2的幂,每次二分其实就相当于在判断二进制下某一位是否能填作1

因此,amid的值实际上就等于k=lmidvalk

唔姆,假设树状数组的vali表示等于i的数的个数,那么amid的值等同于值在[l,mid]范围内的数的个数。

于是就能轻松实现第k大的询问了。

模板:普通平衡树

其实只要能够求第k大,平衡树的其他操作都非常简单了,前驱/后继都可以转化为询问排名再询问第k大的操作。

唯一一个特殊注意点,就是由于这道题中值可能为负,需要把每个数都加上107再操作。

代码class封装,码风毒瘤(尽管在这道题中体现不多)。

/*友情提醒: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 卷] 冰火战士

  • 有两个数组Icei,Firei
  • 每次操作修改某个数组中某个位置上的数。(始终非负)
  • 每次操作完求一个最大的整数k,使得min{ikIcei,ikFirei}最大,并求出这个最大值。
  • 操作数量2×106

很显然,f(k)=ikIceik增大递增,g(k)=ikFireik增大递减。

那么根据初中函数知识就可以知道,要使最小值最大,就是求两个函数的交点。

题目很简单,可惜这道题居然卡线段树!

这时候就要请出树状数组上二分啦!

每次先二分出最大的k满足f(k)<g(k),由于k是整数因此不一定能找到交点,还要比较f(k)g(k+1)谁更大。

求出最大值之后,还要再次树状数组上二分求出最大的k

大致思路差不多就这样啦。

Postscript

树状数组最大的优点就是代码短、常数小,因此真的真的是个非常美丽的算法哦!

posted @   童女讴歌的荣华帝政  阅读(785)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
Preface树状数组上二分模板:普通平衡树[省选联考 2020 A/B 卷] 冰火战士Postscript
点击右上角即可分享
微信分享提示