树状数组 学习笔记

树状数组可以用来求区间元素的和。

与前缀和做法不同,它支持值的修改。

比如说,现在我有一个数列a,要求你维护这个数列,使其支持两个操作。

1.改变数列第k项的值

2.查询从第i项到第j项的总值

暴力做法总是过不了所有点的,如果使用暴力,虽然操作1是O(1)的,但是操作2是O(n)的,没人对此复杂度满意。

假设原数列为a,我们的树状数组为c,那么,应该有下图的情况。

可以看出,每一个叶节点对应数组中的某个元素。

c[i]为第i列树上最高的那个点。

数组c就是树状数组。

  

不难看出

对于每一个c[i],其值总是决定于其两个子节点,也就是每一个c[i]都是两个子节点的值的和。

现在有一个特殊操作,把下标转化成二进制,就有下图所示的样子

  

可以发现,叶节点的二进制位,其最低位必定是1,我们约定,这些节点上的c数组代表的值是只有一位的。

而对于最后两位是10的位,也就是c[2]和c[6],其位于二叉树的倒数第二层,我们约定,这些节点上的c数组所代表的值也是其下面所有叶节点的值之和。可以看出,在这一层的节点控制2个叶节点。

最后三位是100的位,也就是上图的c[4],其位于二叉树的倒数第三层,这一层的节点控制4个叶节点,c数组同理可以得出。

同样的,最后四位是1000的位,其位于二叉树的倒数第四层,它控制8个叶节点。

我们能不能扩展到一般情况呢?

可以。我们假设有一个二进制数m,从最低位向最高位数,如果拥有n个‘0’位,那么这个节点将控制2^n个叶节点,其上的c数组代表的是[m-2^n+1,m]的区间和。

那么2^n应该怎么求呢?有一个叫lowbit的东西,它能取得最低位的1表示的数。

那么lowbit的实现方法?

*贴代码

可以证明,2^n = m & (-m) (位运算)

如果在改动a数组之后,还要花O(n)时间去修改c数组,那么树状数组就没有任何意义了,因为无法得到性能的提升,实际上,树状数组可以在O(logn)的时间内完成一次修改。

因为改动一次a,没有必要去把整个的c数组改动,只需改动一部分即可。

假如我们要改动a[3],那么显然的,我们要改动的c数组应该是c[3],c[4]和c[8],因为只有这几个点控制3号叶节点,其他的点不控制3号叶节点所以不受影响。

可以看出,c[3],c[4],c[8]是3号节点的祖先。

我们推广到一般情况,对于一次修改操作,我们怎样才能得知c数组的变化呢?

由之前二进制位的讨论,我们知道,对于一个点,这个点控制的叶节点大于1,那么这个点应该是某个点的父亲节点。

那么,一般的,如果一个a[i]发生改变,那么其对应的节点c[i]便也会发生改变,c[i]的父亲节点也会发生改变,c[i]的父亲节点的父亲节点也会发生改变……等等

这样。每次修改只有O(logn),达到预期的性能要求。

时间: 2024-11-10 04:37:50

树状数组 学习笔记的相关文章

树状数组学习资料1

1 一维树状数组 1 什么是树状数组 树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+...+A[n]的时,间是log级别的,而且是一个在线的数据结构. 2 树状数组作用 我们经常会遇到动态连续和查询问题,给定n个元素A[1~N],让我们求sum[L,R] = A[L]+...+A[R],或者更改A[i]的值. 假设数据很小的时候,那么我们利用暴力就可以搞定,这个时候更改A[i]的复杂度为O(1),但是求和的复杂度为O(n),如果有m次求和就是

树状数组的笔记√(hzwer blog)

1 int lowbit(int x) 2 { 3 return x&(-x); 4 } lowbit()的返回值就是 2^k 次方的值. 求数组的和的算法: (1)首先,令sum=0,转向第二步: (2)接下来判断,如果 n>0 的话,就令sum=sum+cn转向第三步,否则的话,终止算法,返回 sum 的值: (3)n=n – lowbit(n)(将n的二进制表示的最后一个零删掉),回第二步. 1 int Sum(int n) 2 { 3 int sum=0; 4 while(n>

树状数组学习

转载自:http://blog.csdn.net/int64ago/article/details/7429868 写下这个标题,其实心里还是没底的,与其说是写博帖,不如说是做总结.第一个接触树状数组还是两年前,用什么语言来形容当时的感觉呢?……太神奇了!真的,无法表达出那种感觉,她是那么的优雅,10行不到的代码,却把事情干的如此出色!没有了解她原理的前提下即使把代码倒背如流也理解不了!其中,我就是一直没搞懂地在使用她.时隔两年,又无意遇到了她,可能是两年的代码经验的积累,有了些新的认识,可以自

树状数组学习(一维)

算法描述 可以对给定序列进行查询和修改 查询:主要用来查询任意两位之间数据和 修改:修改单项数据值 时间复杂度:log(n) 算法思想 1.数组的构建 定义 数组C A C1 = A1 C2 = A1 + A2 C3 = A3 C4 = A1 + A2 + A3 + A4 C5 = A5 C6 = A5 + A6 C7 = A7 C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8 - 不难看出,其实是实现了对A的数据的分别管辖,Cn的管辖数量为 将n化为二进制数

学习笔记——二维树状数组

不知道为什么,就是想把这个坑给填了... 二维树状数组,本质上还是树状数组,只是在一维的基础上变成了二维... 单点修改  1到i,j查询和一维基本一样,直接上代码 #include<iostream> #include<cstdlib> #include<cstdio> #include<algorithm> #define N 3010 using namespace std; int a[N][N],n; inline int lowbit(int x

【算法学习笔记】40.树状数组 动态规划 SJTU OJ 1289 扑克牌分组

Description cxt的扑克牌越来越先进了,这回牌面的点数还可以是负数, 这回cxt准备给扑克牌分组,他打算将所有的牌分成若干个堆,每堆的牌面总和和都要大于零.由于扑克牌是按顺序排列的,所以一堆牌在原牌堆里面必须是连续的.请帮助cxt计算一下,存在多少种不同的分牌的方案.由于答案可能很大,只要输出答案除以1,000,000,009的余数即可. Input Format 第一行:单个整数:N,1 ≤ N ≤ 10^6 第二行到N + 1行:在第i + 1行有一个整数:Ai, 表示第i张牌牌

hdu 5249区间第k大(学习了下树状数组的搞法)

KPI Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 205    Accepted Submission(s): 70 Problem Description 你工作以后, KPI 就是你的全部了. 我开发了一个服务,取得了很大的知名度.数十亿的请求被推到一个大管道后同时服务从管头拉取请求.让我们来定义每个请求都有一个重要值.我的K

【学习整理】树状数组 区间修改+查询

前言:对于区间修改和区间查询这样的简单问题,打一大堆线段树确实是不划算,所以学习了区间修改+区间修查询的树状数组. 我们定义 为原数列, ,显然  . 若想要将区间 的数全部 则只需要将 , 即可. 所以,我们维护一个数组 在将区间 的数全部 则还需同时将 , . 结论: 例题:CODEVS1082 线段树练习3 http://codevs.cn/problem/1082/ #include<iostream> #include<cstdio> #include<cstdli

树状数组review学习

学习参考: https://blog.csdn.net/flushhip/article/details/79165701 https://blog.csdn.net/moep0/article/details/52770728 树状数组在寒假集训的时候其实有讲过,但是当时只是有了板子,没有很深地去学习理解,现在回来想去理解一下这个很好用的数据结构. 树状数组的作用 树状数组是对于一个用来处理动态更新.动态统计区间问题的一种良好的数据结构,查询和修改复杂度都为O(logn)的数据结构. 树状数组