树状数组~讲解

树状数组(Binary Indexed Tree(B.I.T))

是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只

能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。-------摘自百度百科

对于树状数组这种数据结构,关键词一是树状,另一个则是数组。

请看下图:

我们令每个叶节点代表每一个元素。

现在我们变形一下,顺便加上数组的编号:

a数组是原数据。

b数组的节点,代表其所有子节点之和。

b[1] = a[1]

b[2] = a[1] + a[2]

b[3] = a[3]

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

b[5] = a[5]

b[6] = a[5] + a[6]

b[7] = a[7]

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

这时,我们再把b数组转化成二进制编码:

可以发现:

1=(001)      b[1] = a[1]

2=(010)      b[2] = a[1] + a[2]

3=(011)      b[3] = a[3]

4=(100)      b[4] = a[1] + a[2] + a[3] + a[4]

5=(101)      b[5] = a[5]

6=(110)      b[6] = a[5] + a[6]

7=(111)      b[7] = a[7]

8=(1000)    b[8] = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8]

对照式子可以发现  b[i] = a[i-2^k+1] + a[i-2^k+2] + ......a[i](k为i的二进制中从最低位到高位连续零的长度)例如i=8时,k=3,i=7时,k=0,i=2时,k=1。

由此我们引出了lowbit函数:

lowbit(i) = 2^k

b[i] = a[i-lowbit(i)+1] + a[i-lowbit(i)+2] + ......a[i]

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

其实......很短......背吧......

接着是修改函数:

1 void add(int k, int num)
2 {
3     while(k<=n)
4     {
5         BIT[k]+=num;
6         k += lowbit(k);
7     }
8 }

BIT就是b数组

在位置k上加上num,我们用lowbit枚举出与k有关的位置,依次加上num。

举个栗子:

比如我们要修改1位置的值。与之有关的是b[1] b[2] b[4] b[8]。

第一次 k = 1,BIT[1] += num后,lowbit(1) = 1,k+=1;

第二次 k = 2,BIT[2] += num后,lowbit(2) = 2,k+=2;

第三次 k = 4,BIT[4] += num后,lowbit(4) = 4,k+=4;

第四次 k = 8,BIT[8] += num后,lowbit(8) = 8;

another:

修改3位置的值。与之有关的是b[3] b[4] b[8]。

第一次 k = 3,BIT[3] += num后,lowbit(3) = 1,k+=1;

第二次 k = 4,BIT[4] += num后,lowbit(4) = 4,k+=4;

第三次 k = 8,BIT[8] +=num后, lowbit(8) = 8;

最后是查询操作:

在这里,我们反着来做

 1 int query(int k)
 2 {
 3     int sum = 0;
 4     while(k>0)
 5     {
 6         sum+=BIT[k];
 7         k-=lowbit(k);
 8     }
 9     return sum;
10 }

跟修改的原理是一样的,不过是反着做,比如查询7位置之前所有的和,就是BIT[7] + BIT[6] + BIT[4]。

这里你会想,我们的查询函数是查询从 1——k 的值的和,要是查询一个区间 l——r(包含 l 和 r 的值) 的话怎么办?

我们利用前缀和的思想,查询 l——r 就等于query(r) - query(l-1)。

所以,luogu P3374 树状数组的模板1,就解决啦~

https://www.luogu.org/problemnew/show/P3374

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 int n,m;
 6 int BIT[500070],a[500070];
 7 int lowbit(int x)
 8 {
 9     return x&-x;
10 }
11 void add(int k, int num)
12 {
13     while(k<=n)
14     {
15         BIT[k]+=num;
16         k += lowbit(k);
17     }
18 }
19 int query(int k)
20 {
21     int sum = 0;
22     while(k>0)
23     {
24         sum+=BIT[k];
25         k-=lowbit(k);
26     }
27     return sum;
28 }
29 int main()
30 {
31     scanf("%d%d",&n,&m);
32     for(int i=1;i<=n;i++)
33     {
34         scanf("%d",&a[i]);
35         add(i,a[i]);//初始化元素,当然是每个位置i加上a[i]啦
36     }
37
38     for(int i=1;i<=m;i++)
39     {
40         int c;
41         scanf("%d",&c);
42         if(c == 1)
43         {
44             int k,num;
45             scanf("%d%d",&k,&num);
46             add(k,num);
47         }
48         else
49         if(c == 2)
50         {
51             int l,r;
52             scanf("%d%d",&l,&r);
53             printf("%d\n",query(r)-query(l-1));
54         }
55     }
56     return 0;
57 }

原文地址:https://www.cnblogs.com/MisakaAzusa/p/8666551.html

时间: 2024-08-03 20:57:39

树状数组~讲解的相关文章

poj 1195 二维树状数组 及二维树状数组模板

http://poj.org/problem?id=1195 求矩阵和的时候,下标弄错WA了一次... 求矩形(x1,y1) (x2,y2)的sum |sum=sum(x2,y2)-sum(x1-1,y2)-sum(x2,y1-1)+sum(x1-1,y1-1) 二维树状数组讲解:http://blog.csdn.net/u011026968/article/details/38532117 二维树状数组模板: /*========================================

火柴排队(NOIP2013)(附树状数组专题讲解(其实只是粗略。。。))

原题传送门..(9018上不去.明天再来搞.) 首先,这道题目是一道神奇的题. 看到这道题,第一眼就觉得2个数组排个序,然后一一对应的时候一定差值最小. 由于我们可以将这2个数列同时进行调换. 所以我们先把2个数列排个序. 第二个序列中的数组的下标都指向第一个数组中的数的原来位置(其实就是离散化(真是啰嗦..)) 离散化之后,我们就变成了一个混乱的数列变成升序数列的操作次数是多少. 然后自然就会想到逆序对.每次变换之后逆序对的个数最多只能-1: 所以答案就是数列中逆序对的个数. 然后就是求逆序对

树状数组模板

看了很多讲解仍然不明就里,感觉反正代码很短,暂时当模板背过好了. //树状数组 单点修改 区间查询 const int maxn=1005; int tree[maxn],n; void init() { for (int i=1;i<=n;i++) tree[i]=0; } //初始化一个长度为n的树状数组,n为全局变量 int lowbit(it k) { return k&-k; } void add(int k,int x) //给位置k加x { while (k<=n) {

树状数组彻底入门

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; } 这篇笔记 会详细的讲解,使得队员们对树状数组彻底入门  而不是懵懵懂懂. 以上先给出 最常见的,三个

树状数组基础

关于树状数组的讲解推荐<算法竞赛入门经典训练指南> 一维版本: 洛谷3374 链接:https://www.luogu.org/problem/show?pid=3374 分析:树状数组裸的模板题 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=500000+10; 6 int c[maxn]; 7 int

树状数组区间更新

在今天的文章开始之前,给大家提一个建议,由于线段树和树状数组这两个结构的分析有很多联系,因此,建议没有看前几篇文章的朋友一定需要了解一下前面的内容.链接如下: 线段树+RMQ问题第二弹 线段树第二弹(区间更新) 树状数组(Binary Indexed Tree,BIT) 上篇文章我们讨论了树状数组的基本结构以及它最擅长的两个功能:单点更新和区间求和,今天,我们来接着上一篇文章的内容继续深入研究.上篇文章我们是将树状数组和线段树进行对比讲解的,既然线段树的章节我们介绍了区间求和.区间最值.单点更新

BZOJ 1227 [SDOI2009] 虔诚的墓主人 离线+树状数组+离散化

鸣谢:140142耐心讲解缕清了我的思路 题意:由于调这道题调的头昏脑涨,所以题意自己搜吧,懒得说. 方法:离线+树状数组+离散化 解析:首先深表本蒟蒻对出题人的敬(bi)意(shi).这道题简直丧心病狂,看完题后大脑一片空白,整个人都不好了,刚开始的思路是什么呢?暴力思想枚举每个墓碑,然后计算每个墓碑的虔诚度,然后再来统计.不过看看数据范围呢?10^9*10^9的矩阵,最多才10^5个树,光枚举就已经超时了,所以肯定不行.(不过要是考试真没思路我就那么搞了- -!) 然后也想到来枚举墓碑,不过

求逆序数模板(树状数组+离散化 || 归并排序法)

一篇不错的讲解:http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html 代码如下:(树状数组+离散化) #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn=500017; int n; int aa[maxn

Hdu 1394 Minimum Inversion Number(线段树或树状数组)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 11981    Accepted Submission(s): 7321 Problem Description The inversion number of a given number sequence a1, a2, ..., a