树状数组 小白篇(1)

身为一名弱省oier中的mengbier,简单讲一下我是怎么学会基础的树状数组的

不算华丽的分割线


  • 树状数组(Binary Indexed Tree(B.I.T), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。
  • 其发明者命名为Fenwick树,最早由Peter M. Fenwick于1994年以《A New Data Structure for Cumulative Frequency Tables》为题发表在 “SOFTWARE PRACTICE AND EXPERIENCE” 。
  1. 主要用于查询任意一段数据中的所有元素之
  2. 经过简单修改可以在log(n)的复杂度下进行范围修改。

也就是说你通过一系列神奇的操作可以实现在一个数列{an}中,修改其中一项ax的值(还可以是一段),并求出{an}前n项和(也可以是任意一段)。

额妹子!!!对不对?

我们知道 “树状数组” 这个名词的定语是 “树状” ,它只是用来修饰数组的对不对?

所以 “树状数组” 就是一串有特异功能的数组!

举个例子:

      正常的一个数组{an}:a[1]=1, a[2]=2, a[3]=3, a[4]=4, a[5]=5, a[6]=6 ,a[7]=7, a[8]=8, a[9]=9

                你要得到第n个数,就直接用a[n]来表示吧(废话)

                但你要算前x项和{sn}的话,就只能从a[1]一项一项加到a[x], 显然当需要大量计算{sn}时,这种方法非常耗时!!!

                于是机智的你想到了先计算出每一个sn来,但是当{an}需要修改怎么办?

                难道要每一个有关的sn都修改吗?

      于是大佬们YY出了树状数组

      神奇的数组{cn}: c[1]=1, c[2]=3, c[3]=3, c[4]=10, c[5]=5, c[6]=11, c[7]=7, c[8]=36, c[9]=9

              我说{cn}里能表达{an}所有信息,你信不信?

          神出鬼没的分界线



        

        设节点编号为x,那么这个节点管辖的区间为2^k(其中k为x二进制末尾0的个数)个元素。这个区间最后一个元素为Ax,

        所以很明显:Cn = A(n – 2^k + 1) + ... + An

        也就是说例如:

              c[1], 1的二进制数为1,k=0,它就管长度为2^0的区间的和,暨c[1]=a[1]

              c[2], 2的二进制数为10,k=1,它就管长度为2^1的区间的和,暨c[2]=a[1]+a[2]

              c[4], 4的二进制数为100,k=2,它就管长度为2^2的区间的和,暨c[4]=a[1]+a[2]+a[3]+a[4]

              c[6], 6的二进制数为110,k=1,它就管长度为2^1的区间的和,暨c[6]=a[5]+a[6]

              c[9], 9的二进制数为1001,k=0,它就管长度为2^0的区间的和,暨c[9]=a[9]

              强烈建议读者用纸笔模拟一遍!!!

        有一个函数可以简单的计算出这个2^k

        

1 int lowbit(int x)
2 {
3     return x&-x;
4 }

        不信就用笔算一算

        那我现在要求前n项和怎么办?

        那就从c[n]开始,不重复的加完数据!

        例如:

            我要求前6项和,那就从c[6]开始加,发现c[6]已经代表了2项,加完c[6],就加c[6-2],加c[4]的值,发现c[4]代表了4项,此时就加完前6项

        还是看代码吧

 1 int sum(int n)
 2 {
 3      int ans=0;
 4      while(n>0)
 5      {
 6               ans+=c[n];
 7               n-=lowbit(n);    //加完了该项能表示的数,就加还没表达到的数
 8      }
 9
10      return ans;
11 }

      那要改一项的值就要改{cn}中每个有关的项

1 void add(int x,int v)    //x位置加v
2 {
3      while(x<=n)        //n为边界,一共有多少个数
4      {
5                  c[x]+=v;
6                  x+=lowbit(x);     //下一个可以管辖该点的数组下标
7      }
8 }        

那从a[x]+a[x+1]+a[x+2]+……+a[y-1]+a[y],就可以表示为sum(y)-sum(x-1);

就是任意连续数列和了

          神出鬼没的分界线



        在下的文字功底还是不行,可能讲的不是很好,请见谅

提供一些有关树状数组的题目:

新手练习1

新手练习2(需要抽象化模型)

时间: 2024-11-08 20:17:07

树状数组 小白篇(1)的相关文章

树状数组 小白篇(2)暨区间修改

这篇主要来讲一讲树状数组的区间修改 因为一个一个点改,毫无疑问耗时太长 所以,机智的人类YY出了用差分来表示数组 为了便于理解,简单一点数组{an}:a[1]=0, a[2]=0, a[3]=0, a[4]=0, a[5]=0, a[6]=0 ,a[7]=0, a[8]=0, a[9]=0 用差分思想,delta[x]表示a[x]-a[x-1] 显然,一开始delta[]=0 我们先计算出前n项和{sn}来 然后别眨眼,下面就是见证奇迹的时刻! 有个分界线显得正式一点 我们要把a[3]到a[6]

树状数组入门篇2

树状数组的高效性主要就是通过将一条线段分成若干个小线段(其中每个小线段存储着2^k大小的区间和,这就将区间和问题复杂度降到了logn),而不是一个一个单一的点 add()操作修改了单点的值,同时对之后的父亲节点进行了更新(之所以只更新该点以及该点的父亲节点而不更新该点的非父亲节点,是因为求和时父亲节点直接包含该点所以会跳过该点,也就不能接受该点,所以就需要自身更新,而非父亲节点在求和时会直接加上该点,所以就不需要进行更新) 正是由于add()操作对父亲节点以及非父亲节点的区别才使每次的sum()

小白初理解树状数组

ACM的在线测试里经常涉及到大量数据的的修改,求和等操作,这里介绍一种方法——树状数组. 树状数组,是一个查询和修改复杂度都为log(n)的数据结构.主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值:经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值.可以用一张图来弄懂什么是数组数组. 原数组A[n],树状数组C[n]; 如果n为奇数:Cn=An; 如果n为偶数:Cn = A(n – 2^k + 1) + ... + An,k为n的二进制数

(新人的第一篇博客)树状数组中lowbit(i)=i&amp;(-i) 的简单文字证明

第一次写博好激动o(≧v≦)o~~初一狗语无伦次还请多多指教 先了解树状数组http://blog.csdn.net/int64ago/article/details/7429868感觉这个前辈写的很好 但是里面提到的lowbit则是一笔带过,那么lowbit究竟是个什么鬼呢??? 首先明白一个概念,计算机中-i=(i的取反+1),也就是i的补码 而lowbit,就是求(树状数组中)一个数二进制的1的最低位,例如01100110,lowbit=00000010:再例如01100000,lowbi

数据结构——树状数组篇

原地址:My CSDN Blog  那边还未通过审核,先看这边的吧…… 线段树是一个很好的维护区间关系的这样的一个数据结构,但是,很多时候我们可以用更小空间.更快速度(更大尺寸呢.,全景天窗,五菱宏光?)的数据结构来维护一个前缀关系. ??? 上面的这张图是表示的一个有8个叶子节点的线段树,接下去,我们给它进行一个变形: ??? 然后呢,我们把2.4.6.8.……这样的元素推到最顶端的空上边去,想让2表示1-2这段区间,让4表示1-4这段区间,让6表示5-6这段区间,让8表示1-8这段区间. 然

POJ 2155 Matrix(二维树状数组,绝对具体)

Matrix Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 20599   Accepted: 7673 Description Given an N*N matrix A, whose elements are either 0 or 1. A[i, j] means the number in the i-th row and j-th column. Initially we have A[i, j] = 0 (1

树状数组求逆序对:POJ 2299、3067

前几天开始看树状数组了,然后开始找题来刷. 首先是 POJ 2299 Ultra-QuickSort: http://poj.org/problem?id=2299 这题是指给你一个无序序列,只能交换相邻的两数使它有序,要你求出交换的次数.实质上就是求逆序对,网上有很多人说它的原理是冒泡排序,可以用归并排序来求出,但我一时间想不出它是如何和归并排序搭上边的(当初排序没学好啊~),只好用刚学过的树状数组来解决了.在POJ 1990中学到了如何在实际中应用上树状数组,没错,就是用个特殊的数组来记录即

树状数组彻底入门

int lowbit(int t) { return t&(-t); } void add(int x,int y) { for(int i=x;i<=n;i+=lowbit(i)) tree[i]+=y; } int getsum(int x) { int ans=0; for(int i=x;i>0;i-=lowbit(i)) ans+=tree[i]; return ans; } 这篇笔记 会详细的讲解,使得队员们对树状数组彻底入门  而不是懵懵懂懂. 以上先给出 最常见的,三个

[bzoj2124]等差子序列(hash+树状数组)

我又来更博啦 2124: 等差子序列 Time Limit: 3 Sec  Memory Limit: 259 MBSubmit: 941  Solved: 348[Submit][Status][Discuss] Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. O