【算法#3】树状数组&二叉索引树

其实是数据结构。

智推连续几天给我推树状数组的模板,还放在第一位……

对着蓝书的图看了好几天才看懂,树状数组的另外一个名字是二叉索引树,指通过把一个数组抽象的变形成树状的以求得到树形数据结构的效果。有人说是线段树的阉割版,我不太清楚,树状数组应该是不支持区间修改加速的。

首先我们需要理解lowbit的概念,它指的是一个数转成二进制后位数最低的那个1表示的值。它具有一个特殊的性质但是为什么具有这个性质是无须证明也不用了解的。

然后我们画一个图,在一定范围内按lowbit的大小从上向下逐层分布,一层中按编号排序。不难发现这是棵二叉树。(见蓝书)我们让每个节点水平延伸lowbit(它的)-1长度的条,包括该节点在内的长条表示它覆盖的区间内的所有点的和或者其他什么性质。

并不一定非要纠结于这个图,然后我们发现lowbit的一个特殊性质就是一个区间的编号减去它的lowbit等于不包含这个区间的且在该区间前面的第一个(最靠右)的区间。一个区间的编号加上它的lowbit等于包含该区间的且最小(最靠左)的区间的编号。很玄学,对吧。

我们知道了lowbit的概念和它的性质,那么怎么求lowbit呢?我们知道,计算机中采用补码,也就是取一个数的负数等于这个数取反加一,以100为例:

01100100(100)

10011011(-99)//是-101还是-99?

10011100(-100)

00000100(对-100和100按位取与)

所以\(lowbit(x)=x&-x\)。更玄学了对吧。

对于查询区间和,在当前区间编号非零的情况下使结果加上当前区间,再去往上一个区间(减去lowbit)。可以得到以x为起点的前缀和。

对于单点修改,在当前区间编号小于等于n的时候使当前区间修改值。

至于预处理……对于每个元素,进行n次add修改操作就好了。板子贴在这里。

int?lowbit(int?x){
????return?x&-x;
}
int?sum(int?x){
????int?res=0;
????while(x){
????????res+=c[x];
????????x-=lowbit(x);
????}
????return?res;
}
void?add(int?x,int?d){
????while(x<=n){
????????c[x]+=d;
????????x+=lowbit(x);
????}
}

原文地址:https://www.cnblogs.com/Schwarzkopf-Henkal/p/11830120.html

时间: 2024-10-12 16:28:53

【算法#3】树状数组&二叉索引树的相关文章

二叉索引树(树状数组)入门(一)

二叉索引树,即树状数组,被某神犇称之为是最漂亮的数据结构,所以蒟蒻北篱也去学习了一下传说中的树状数组. 限于蒟蒻北篱的语言表达能力太差(其实是懒),于是引用了度娘的一段对树状数组的解释 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值:经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅

二叉索引树BIT

定义     二叉索引树,binary index tree,又名树状数组,或Fenwick Tree,因为本算法由Fenwick创造.     对于数组A,定义Query(i,j) = Ai +Ai+1 + - + Aj.     比较好的做法:使用前缀和,Sum(j) – Sum(i-1)即可得到Query(i,j)     BIT即为解决此类区间查询而大展身手,因为预处理时间为O(n),之后的查询时间为O(1),是属于典型的在线算法(关于在线算法,通俗地可以理解为,做一次预处理,提供多次"

C++实用数据结构:二叉索引树

看下面这个问题(动态连续和查询): 有一个数组A(长度为n),要求进行两种操作: add(i,x):让Ai增大x: query(a,b):询问Aa+Aa+1+...+Ab的和: 若进行模拟,则每次query操作的最坏的时间复杂度为O(n),在n较大时速度较慢.用前缀和也不能提高效率(每次add操作最坏为O(n)).有一种数据结构,可以在O(n)时间里初始化,用O(logn)的速度执行add操作或查询前缀和,从而执行query操作. 首先,我们来介绍“lowbit”.对于一个数x,lowbit(x

树状数组求区间最大值(树状数组)(复习)

如题. 当遇到单点更新时,树状数组往往比线段树更实用. 算法: 设原数序列为a[i],最大值为h[i](树状数组). 1.单点更新: 直接更新a[i],然后再更新h[i].若h[i]的值有可能改变的,则表示区间一定包含i结点.那么就两层lowbit更新所有可能的h. 单点更新时间复杂度O(logn*logn) 1 void update(int x) 2 { 3 while(x<=n) 4 { 5 h[x]=a[x]; 6 for(int i=1;i<lowbit(x);i<<=1

HDU 4417 Super Mario ( 超级马里奥 + 主席树 + 线段树/树状数组离线处理 + 划分树 )

HDU 4417 - Super Mario ( 主席树 + 线段树/树状数组离线处理 + 划分树 ) 这道题有很多种做法,我先学习的是主席树.后面陆续补上线段树离线和划分树 题目大意就是给定一个区间给定一个数列,每次要求你查询区间[L,R]内不超过K的数的数量 主席树做法: 最基本的是静态第k大,这里是求静态的 <= K,差不多,在Query操作里面需要修改修改 先建立size棵主席树,然后询问的时候统计的是 第R棵主席树中[1,K]的数量 - 第L-1棵主席树中[1,K]的数量 注意这里下标

HDU 4325 离散化+树状数组 或者 不使用树状数组

题意:给出一些花的开放时间段,然后询问某个时间点有几朵花正在开放. 由于ti<1e9,我们需要先将时间离散化,然后将时间点抽象为一个数组中的点,显然,我们需要进行区间更新和单点查询,可以考虑线段树与树状数组两种做法,一般的,树状数组是用来维护区间和与单点修改的,那么,如何通过树状数组进行区间更新和单点查询呢,联想到差分数组,差分数组可以在o(1)的时间内进行区间的更新,但是单点查询的效率为o(n),显然不能用于处理此题,这时,考虑树状数组维护差分数组,就可以o(logn)地进行区间更新(更新差分

实用数据结构---树状数组(二叉索引树)

树状数组适用于动态连续和查询问题,就是给定一个区间, 查询某一段的和或者修改某一位置的值. 关于树状数组的结构请去百度百科,否则将看不懂下面内容 我们看这个题 士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5 描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常想知道第m号到第n号士兵的总杀敌数,请你帮助小工来回答南将军吧. 南将军的某次询问之后士兵i可能又杀敌q人,之后南将军再询问的时候,需要考虑到新

浅析树状数组(二叉索引树)及一些模板

树状数组 动态连续和查询问题.给定一个n个元素的数组a1.a2.……,an,设计一个数据结构,支持以下两种操作:1.add(x,d):让ax增加d;2.query(l,r):计算al+al+1+…+ar 如何让query和add都能快速完成呢?方法有很多,这里介绍的便是树状数组.为此我们先介绍lowbit. 对于正整数x,我们定义lowbit(x)为x的二进制表达式中最右边的1所对应的值(而不是这个比特的序号).比如,38288的二进制1001010110010000,所以lowbit(3828

二叉索引树 树状数组

树状数组 维护一个序列 a1 a2 a3……an 支持两种操作: 1. sum(int a,int b) a~b的区间和 2. add(int x,int d) 第x个数增加d 设lowbit(x)为x的二进制最右边的1表示的值 如lowbit(38288)=lowbit(1001010110010000)=10000=16 对于节点i,如果它是左子结点,父节点为i+lowbit(i):如果它是右子节点,那么父节点是i-lowbit(i) 定义一个数组C C1 = A1 C2 = A1 + A2