树状数组

忘是亡心i 2022-07-26 08:44 356阅读 0赞

树状数组

树状数组:用线性数据结构的方法解决动态统计子树权和的问题。

类似于线段树,将区间分成小段,方便计算权和。

举个栗子,将a数组构造成树状数组c。

如果a数组中共有8个元素a[1]~a[8],注意这里数组的下标要从1开始,那么:

c[1]=a[1]

c[2]=a[1]+a[2]

c[3]=a[3]

c[4]=a[1]+a[2]+a[3]+a[4]

c[5]=a[5]

c[6]=a[5]+a[6]

c[7]=a[7]

c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]

下面说一下为什么c数组是这样的,还有对c数组的一些操作。

一、c数组的来历

线段树是直接递归二分来划分,而树状数组需要通过lowbit(i)这个函数来确定划分范围。

  1. int lowbit(int x)//计算二进制数x右方的第一位1对应的权
  2. {
  3. return x&(-x);
  4. }

①c[k]存储从a[k]开始向前数lowbit(k)个元素之和,即c[k]=a[k]+a[k-1]+a[k-2]+…+a[i-(2^k)+1]。

②lowbit(k)=k&(-k),在二进制中,-k是取反加1,这个函数是求整数k的二进制表示中右边第一个1在二进制中代表的权值。

例如:

  1. #include<iomanip>
  2. #include<iostream>
  3. #include<stdlib.h>
  4. #include<cstdio>
  5. #include<cmath>
  6. using namespace std;
  7. int main()
  8. {
  9. char str[25];
  10. int i;
  11. for(i=1; i<=10; ++i)
  12. {
  13. itoa(i,str,2);
  14. cout<<"i="<<i<<" "<<(i&-i)<<" 二进制 "<<str<<endl;
  15. }
  16. return 0;
  17. }

运行结果:

Center

由图,可以得到i对应的lowbit(i)值。

所以:

当i=1时,lowbit(1)=1,c[1]=a[1];

当i=2时,lowbit(2)=2,c[2]=a[2]+a[1],c[2]由a[2]及其之前共计lowbit(2)=2个数相加得到;

当i=3时,lowbit(3)=1,c[3]=a[3];

当i=4时,lowbit(4)=4,c[4]=a[4]+a[3]+a[2]+a[1],c[4]由a[4]及其之前共计lowbit(4)=4个数相加得到;

………………………………

下面依次类推,所以就得到上面c数组的各个值。

二、调整整棵子树

从c[i]依次调整这棵子树,数组下标是i+=lowbit(i)

举个栗子:当改变a[4]的值的时候,c[4]与a[4]、a[3]、a[2]、a[1]都有关,这四个都要改变权值,

那么计算出4、3、2、1这四个数组下标的公式就是i+=lowbit(i)。

例如如下代码:

  1. void change(int x)//x是a数组的下标
  2. {
  3. int i;
  4. if(条件满足)
  5. for(i=x; i<cnt; i+=lowbit(i))//调整a[x]这一棵子树
  6. c[i]改变;
  7. }

三、计算子树权值之和

与上面的情况类似,依次找出这个节点相关的所有节点,即相应的数组下标,再累加起来就是权值之和。

例如如下代码:

  1. int sum(int x)//计算节点x子树权值之和
  2. {
  3. int i,res=0;//res是权值和
  4. for(i=x; i>0; i-=lowbit(i))
  5. res+=c[i];
  6. return res;
  7. }

四、二维树状数组

和一维的树状数组一样,就是更新和查询稍微作出一点改变。可以看一下模板入门题(传送门→):POJ 1195

更新和查询的代码改变如下:

  1. void update(int x,int y,int num)
  2. {
  3. int i,j;
  4. for(i=x; i<=n; i+=lowbit(i))
  5. for(j=y; j<=n; j+=lowbit(j))
  6. c[i][j]+=num;
  7. }
  8. int sum(int x,int y)
  9. {
  10. int i,j,res=0;
  11. for(i=x; i>0; i-=lowbit(i))
  12. for(j=y; j>0; j-=lowbit(j))
  13. res+=c[i][j];
  14. return res;
  15. }

以上就是我自己学习了一上午之后对树状数组的简单的认识,下面贴一道入门题POJ 3321-Apple Tree(←这里是传送门),希望这道题对理解树状数组有很好的帮助。

发表评论

表情:
评论列表 (有 0 条评论,356人围观)

还没有评论,来说两句吧...

相关阅读

    相关 树状数组

    目录: 1. 概念 2. 应用 3. 充分性 4. 建立树状数组 5. 典题分析   一.概念 数组来模拟树形结构。那么、为什么不直接建树?没必要,因为树

    相关 树状数组

    1、概述 树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组\{a

    相关 树状数组

    树状数组 树状数组:用线性数据结构的方法解决动态统计子树权和的问题。 类似于线段树,将区间分成小段,方便计算权和。 举个栗子,将a数组构造成树状数组c。 如果a

    相关 树状数组初探

    前言 在前一篇文章:[线段树初探][Link 1] 中我们看了一下线段树的基本思想并且知道了线段树擅长于解决区间问题。其实对于某些区间问题,我们不仅可以用线段树解决,还可

    相关 树状数组实现

    树状数组能够完成如下操作: 给一个序列a0-an 计算前i项和 对某个值加x 时间o(logn) 注意:有人觉得前缀和就行了,但是你还要维护啊,改变某个值,一个一个改