树状数组初步理解

学习树状数组已经两周了,之前偷懒一直没有写,赶紧补上防止自己忘记(虽然好像已经忘得差不多了)。
作为一种经常处理区间问题的数据结构,它和线段树、分块一样,核心就是将区间分成许多个小区间然后通过对大区间的调用来提升效率。因此,我们主要需要了解的就是这种分块方式。
不同于线段树直接将区间不断的进行二分,我们将区间二分的同时将父节点直接放在右区间上,从而形成了一个占用空间很小的树。
在这里插入图片描述
我们仔细观察这张图:根据我们刚才的思想,每个右节点都储存着左右两个子区间的和,即右节点本身就是自己的父节点,而右节点的父节点就是父节点的父节点。想象一下,原本是一个立体的二叉树,我们现在将它向右压,硬生生将一个二维的树压成了一个数组,就得到——树状数组!!!
虽然占用空间大大减小,但是对结点的访问现在变成了一个问题,我们应该怎么访问父亲结点和子节点呢?
仔细观察,我们发现结点n是x个结点的祖先,这个x就是n的二进制的不为零的最小位(不要问我为什么能看出来,我也看不出来啊,也不知道哪位神仙想出来的),我们用一个函数lowbit(n)来表示这个神奇的数字。因为父节点是右偏的(杜撰的专业术语233),所以右边lowbit(n)个元素和这个区间是并列的,并且右边lowbit(n)长的区间最右边的元素是左右两个区间的祖先,它的位置我们很容易得到是n+lowbit(n)——这就是子节点到父节点的访问方式。
结点n是左边lowbit(n)个元素的祖先,所以n-lowbit(n)就是另外一个与n所在区间紧邻而且没有重叠的区间,所以我们想要遍历n以前所有的元素时只需要不断的进行n-lowbit(n)直到n==0表示已经跳出区间,通过这种方法我们能够方便的得到前缀和。
通过上面的总结,我们得到了对树状数组进行访问的初步方法。
lowbit()函数的实现方式一般开来说时lowbit(x)=x&(-x);至于为什么这就涉及到玄学的二进制知识,有兴趣的话自己去了解一下吧。
根据以上分析,我们可以得到初步的对树状数组建立以及维护的方法:
假如我们想要得到的是区间求和:

int lowbit(x)
{return x&(-x);
}
void update(int x,int y)
{for(;x<=n;x+=lowbit(x))	//x+lowbit(x)是包含x元素的父节点 {c[x]+=y;}
}

如何进行查询呢?

int sum(int x)	//x的前缀和 
{int ans=0;for(;x;x-=lowbit(x)){ans+=c[x];}return ans;
} 

如果想要得到中间一段区间的和,不难想到query(x,y)=sum(y)-sum(x-1);
这样,我们就初步实现了树状数组的维护和查询。
下附一道练手题:
POJ - 2352 Stars
大概意思就是说求一个二维数组中处于左下角的元素的个数。乍一看好像是一个二维的树状数组,但是分析一下数据范围就会发现不太现实。而且题目中说给出的数据是有顺序的,因此我们可以分析一下,不难发现(就当作不难吧)后面输入的数据对前面输入的数据是没有影响的,而且对于每一颗星星来讲,处于它下方右边的并没有什么意义,因此我们不妨将这个图形一维化,每个星星的亮度就是所有处于它左边(和它所在位置但不包括它)星星的个数。问题的思路应该很清晰,下附ac代码:

#include<cstdio>
#include<cstring>
using namespace std;const int maxn=32005;
int n;
int c[maxn];
int ans[maxn];int lowbit(int x)
{return x&(-x);
}
void update(int x)
{for(;x<=maxn;x+=lowbit(x)){c[x]++;}
}
int sum(int x)
{int ans=0;for(;x;x-=lowbit(x)){ans+=c[x];}return ans;
}
int main()
{int u,v;memset(c,0,sizeof(c));memset(ans,0,sizeof(ans));scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d",&u,&v);u++;//需要注意树状数组的下标必须从1开始!!!ans[sum(u)]++;update(u);}for(int i=0;i<n;i++){printf("%d\n",ans[i]);}return 0;
}

至于树状数组的区间修改区间查询,将会在树状数组的进一步理解中说明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384212.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux socket编程(二) 服务器与客户端的通信

http://www.cnblogs.com/-Lei/archive/2012/09/04/2670964.html上一篇写了对套接字操作的封装&#xff0c;这一节使用已封装好的Socket类实现服务器与客户端的通信&#xff08;Socket的定义见上篇Socket.h) 服务器端&#xff1a; ServerSocket.h #ifndef SERVERSOCKET_H #defin…

UNIX网络编程:I/O复用技术(select、poll、epoll)

http://blog.csdn.net/dandelion_gong/article/details/51673085 Unix下可用的I/O模型一共有五种&#xff1a;阻塞I/O 、非阻塞I/O 、I/O复用 、信号驱动I/O 、异步I/O。此处我们主要介绍第三种I/O符复用。 I/O复用的功能&#xff1a;如果一个或多个I/O条件满足&#xff08;输…

解决iex -S mix报错

执行iex -S mix命令的时候会遇到如下错误&#xff1a; 执行 mix deps.get 然后就可以运行 iex -S mix了 其中&#xff0c;有可能会出现 按照其网站下载相应文件&#xff0c;复制到项目根目录下&#xff0c;然后执行命令&#xff08;mix local.rebar rebar ./rebar&#xff09;即…

Anker—工作学习笔记

http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html 1、基本知识 epoll是在2.6内核中提出的&#xff0c;是之前的select和poll的增强版本。相对于select和poll来说&#xff0c;epoll更加灵活&#xff0c;没有描述符限制。epoll使用一个文件描述符管理多个描述符&am…

Linux网络编程——tcp并发服务器(I/O复用之select

http://blog.csdn.net/lianghe_work/article/details/46519633 与多线程、多进程相比&#xff0c;I/O复用最大的优势是系统开销小&#xff0c;系统不需要建立新的进程或者线程&#xff0c;也不必维护这些线程和进程。 代码示例&#xff1a; [csharp] view plaincopy #include &…

Linux下的I/O复用与epoll详解

http://www.cnblogs.com/lojunren/p/3856290.html 前言 I/O多路复用有很多种实现。在linux上&#xff0c;2.4内核前主要是select和poll&#xff0c;自Linux 2.6内核正式引入epoll以来&#xff0c;epoll已经成为了目前实现高性能网络服务器的必备技术。尽管他们的使用方法不尽相…

数据结构--顺序栈和链式栈

http://www.cnblogs.com/jingliming/p/4602458.html 栈是一种限定只在表尾进行插入或删除操作,栈也是线性表表头称为栈的底部,表尾称为栈的顶部,表为空称为空栈&#xff0c;栈又称为后进先出的线性表,栈也有两种表示:顺序栈与链式栈顺序栈是利用一组地址连续的存储单元&#xf…

数据结构--双链表的创建和操作

http://www.cnblogs.com/jingliming/p/4602144.html#0-tsina-1-42616-397232819ff9a47a7b7e80a40613cfe1 一、双向链表的定义 双向链表也叫双链表&#xff0c;是链表的一种&#xff0c;它的每个数据结点中都有两个指针&#xff0c;分别指向直接后继和直接前驱。所以&#xff0c…

MYSQL错误代码#1045 Access denied for user 'root'@'localhost'

http://blog.csdn.net/lykezhan/article/details/70880845 遇到MYSQL“错误代码#1045 Access denied for user rootlocalhost (using password:YES)” 需要重置root账号权限密码&#xff0c;这个一般还真不好解决。 不过&#xff0c;这几天调试的时候真的遇到了这种问题&#x…

常量变量以及循环

常量 1.三目运算词 三字母词表达字符???([??)]??<{??>} 2.循环 1).数组元素以及变量在内存中的分配顺序 2)goto语句应用 //电脑关机程序 #include<stdio.h> #include <stdlib.h> #include <string.h> #include <windows.h> int ma…

Linux 环境 C语言 操作MySql 的接口范例

http://www.cnblogs.com/wunaozai/p/3876134.html 接上一小节&#xff0c;本来是计划这一节用来讲数据库的增删改查&#xff0c;但是在实现的过程中&#xff0c;出现了一点小问题&#xff0c;也不是技术的问题&#xff0c;就是在字符界面上比较不好操作。比如要注册一个帐号&a…

数组相关运算

数组的初始化 数组及指针在内存中的存储 一维数组在内存中的存储 有关数组的运算 //一维数组 int a[] {1,2,3,4}; printf("%d\n",sizeof(a));//16这里的a表示的是整个数组,计算出的是整个数组的大小,单位为byte printf("%d\n",sizeof(a 0));/*a没有单独…

gets fgets 区别

http://www.cnblogs.com/aexin/p/3908003.html 1. gets与fgets gets函数原型&#xff1a;char*gets(char*buffer);//读取字符到数组&#xff1a;gets(str);str为数组名。 gets函数功能&#xff1a;从键盘上输入字符&#xff0c;直至接受到换行符或EOF时停止&#xff0c;并将读取…

Shuffle'm Up——简单模拟

【题目描述】 A common pastime for poker players at a poker table is to shuffle stacks of chips. Shuffling chips is performed by starting with two stacks of poker chips, S1 and S2, each stack containing C chips. Each stack may contain chips of several diff…

Fire!——两个BFS

【题目描述】 【题目分析】 看到题目后很清楚是两个BFS&#xff0c;可是我觉得对于火的BFS可以转换成判断&#xff0c;我的做法是将火的位置全部记录下来&#xff0c;然后判断某个位置距离每个火的步数是否小于当前步数&#xff0c;可是错了&#xff0c;还不清楚为什么&#x…

函数调用过程(栈桢)

栈桢 首先来看一段代码 #include<stdio.h> int add(int x, int y) {int z x y;return z; } int main() {int a 10;int b 20;int ret add(a, b);printf("ret %d\n",ret);return 0; } 此处是为了给a,b分别开辟空间,这时栈桢如图所示 两条push命令将a,b变…

整型数据存储

//代码1 #include<stdio.h> int main() {char a -1;signed char b -1;unsigned char c -1;printf("a %d, b %d, c %d", a, b, c);return 0; } 1000 0000 0000 0001 -> -1源码 1111 1111 1111 1110 -> -1反码 1111 1111 1111 1111 -> -1补码 对于…

HDU - 4578Transformation——线段树+区间加法修改+区间乘法修改+区间置数+区间和查询+区间平方和查询+区间立方和查询

【题目描述】 HDU - 4578Transformation Problem Description Yuanfang is puzzled with the question below: There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations. Operation 1: Add c to each number between ax …

[C++基础]034_C++模板编程里的主版本模板类、全特化、偏特化(C++ Type Traits)

http://www.cnblogs.com/alephsoul-alephsoul/archive/2012/10/18/2728753.html 1. 主版本模板类 首先我们来看一段初学者都能看懂&#xff0c;应用了模板的程序&#xff1a; 1 #include <iostream>2 using namespace std;3 4 template<class T1, class T2>5 clas…

使用openssl的md5库

http://blog.csdn.net/sinat_35297665/article/details/78244523 在linux机器上&#xff0c;有一个命令可以计算出文件的md5值&#xff0c;那就是md5sum&#xff0c;如果没有的话&#xff0c;就需要安装RPM包&#xff1a;coreutils。 现在我们使用openssl的库也可以方便的计算出…