树状数组基础

摘要:
树阵列简介如果有任何数据结构可以支持间隔/单点和的更新和查询,那么一个明显的答案就是通用线段树。然后,我们使用离散化的数据下标来构建一个全0树数组。此时,您可以通过树阵列加快计算过程。事实上,线段树也可以用于计算,但在代码量方面,树阵列更好。树数组可以看作是一种数据结构,它具有较少的代码和细节,以保持关联法则支持的间隔运算。

树状数组简介

如果有哪一种数据结构可以支持区间/单点和的更新和查询,一个显而易见的答案就是万能的线段树。但是线段树虽然能支持很多的区间问题,但是代码量有些长。如果我们只是单纯地为了维护区间和其实并不用去专门构建一棵线段树。树状数组作为一种更加简单的,可以维护区间和的数据结构应运而生。

树状数组基本思想

对于数组(A)来说,如果我们要求​Sum(Ai , Aj)的结果,除了使用直接遍历或者是线段树,我们还可以为其定义一个数组(C)像下面这样:

树状数组基础第1张

然后我们可以简单地罗列一个c数组到a数组到映射关系

A数组C数组
A1A1
A2A1+A2
A3A3
A4A1+A2+A3+A4
A5A5
A6A5+A6
A7A7
A8A1+A2+A3+A4+A5+A6+A7+A8
A9A9

我们可以把c数组看成一种特殊的前缀和。一般的前缀和的(s~i~)都是代表着(A~i-A~0) 的值,但是我们的c数组每一位代表着的值是像上面这个表写着的那样。

那么剩下的任务就是找规律了。最先可以看到的是对于(A~i~)​来说,如果i是奇数的话,那么有(C~i~ = A~i~)​。如果i是偶数的话,我们可以发现这样一条规律:设x为 i 的二进制形式中最低位1所代表的值,有(C~i~ = Sum(A~x~ , A~1~))​​​ 。求出x值的函数我们一般称之为lowbit()函数,写法如下:

def lowbit(a):
	return a&(-a)

这样我们就可以完成C数组的维护。假设我们要在保持c数组维护着前缀和的性质的前提下更改A[1]的值(A[1]=A[1]+x),可以按照这样的顺序对C数组进行修改:

C[1]=C[1]+x lowbit(1)==1

C[2]=C[2]+x 1+lowbit(1)==2

C[4]=C[4]+x 2+lowbit(2)==4

C[8]=C[8]+x 4+lowbit(4)==8

写成代码就是这样:

def add(x,pos):
    while pos<=n:
        C[pos]+=x
	    pos=pos+lowbit(pos)

区间查询的思想和单点更新其实差不多(就是单点更新的反向操作),看看代码就能理解了:

def query(pos):
    ans=0
        while(pos>0):
	    ans+=tree[pos]
	    pos=pos-lowbit(pos)
    return ans

树状数组在逆序对上的应用

逆序对问题除了可以通过归并排序进行合并以外还可以用树状数组进行计算

逆序对问题就是求对于数组(A)来说,存在一组i,j使得i<j且(A~i~>A~j~) 我们称i,j为一组逆序对。逆序对问题通常是用归并排序的方式来分治计算,同时也可以用树状数组来计算

可以简单地把逆序对问题看成计算对于每一个数字,它前面有多少个比它大的数字,最后的答案就是对于每个数字的结果的和。首先,我们对输入数据进行离散化,因为输入数据大小可能会很大。然后,我们用离散化之后的数据下标构建一个全0树状数组。对于一个数,所有比它大的数字都可以通过计算下标数组上在他前面的数字的个数。此时就可以通过树状数组来加速计算的过程。

其实用线段树也可以进行计算,不过从代码量上来说还是树状数组更优一些。

树状数组可以被看作一种用来维护结合律支持的区间操作的一种代码量和细节都比较少的数据结构。如果要维护某些不支持结合律(比如说区间众数)之类的值的时候还是好好地打线段树吧

免责声明:文章转载自《树状数组基础》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇svn无法提交用树莓派做3G无线路由器下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

BZOJ 2141 排队(分块+树状数组)

题意 第一行为一个正整数n,表示小朋友的数量;第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;第三行为一个正整数m,表示交换操作的次数;以下m行每行包含两个正整数ai和bi,表示交换位置ai与位置bi的小朋友。输出文件共m行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度(逆序对数)。 1≤m≤2*10^3,1≤...

[POJ1195] Mobile phones(二维树状数组)

题目链接:http://poj.org/problem?id=1195 题意:四种操作: 0:初始化一个S*S的零矩阵 1:点(x,y)是值+A 2:查询一个子矩阵里所有数的和 3:退出 线段树由于不能在两棵树之间传递标记,所以这种求和的操作非常难处理。 改学了一下而为树状数组,发现可是比二维线段树简单多了。 记得之前曾经看过zkw线段树的ppt讲稿,好像...

树状数组与线段树(一)

树状数组: 一共需要三个函数: ①lowbit(int x) ②add(int x,int p) ③query(int x) 1.动态求连续区间和 给定n个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列[a,b]的连续和。 输入格式 第一行包含两个整数n和m,分别表示数的个数和操作次数。 第二行包含n个整数,表示完整数列。 接下来m行,...

hdu-3584 Cube---三维树状数组+区域更新单点查询

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3584 题目大意: 给定一个N*N*N多维数据集A,其元素是0或是1。A[i,j,k]表示集合中第 i 行,第 j 列与第 k 层的值。 首先由A[i,j,k] = 0(1 <= i,j,k <= N)。 给定两个操作: 1:改变A[i,j,k]为...

HDOJ 1166 敌兵布阵树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 18120Accepted Submission(s): 7877 Problem Description C国的死对头A国这段时间正在进行军事演习...

Bzoj 2789: [Poi2012]Letters 树状数组,逆序对

2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Status][Discuss] Description 给出两个长度相同且由大写英文字母组成的字符串A、B,保证A和B中每种字母出现的次数相同。 现在每次可以...