树状数组
来源:互联网 发布:自行车 知乎 编辑:程序博客网 时间:2024/05/16 23:41
题目链接
树状数组推荐看训练指南的说明,90%能看懂。不能看懂再往下看。
求a1,a2...an的和,下面看 CS 97SI: Introduction to Competitive Programming Contests 的PPT里的图片。
思想直接看原文:
Each internal node stores the sum of values of its children
– e.g., Red node stores item[5] + item[6]
Main idea: choose the minimal set of nodes whose sum gives the desired value
I We will see that– at most 1 node is chosen at each level so that the total number of nodes we look at is log 2 n
– and this can be done in O(logn) time
计算方法
//版本2
树状数组是对数组元素修改和查询都是O(logn)的结构,主要用于查询任意两位之间的所有元素之和,可参考百度
最基本的是在有修改元素的情况下求区间内元素和
int lowbit(int t){ return t&(-t);}
lowbit(i):将i转化成二进制数之后,只保留最低位的1及其后面的0,截断前面的内容,然后再转成10进制数
X^:X取反(符号我这里定的,不是什么官方符号)
lowbit(x)实际上是提取x从左往右数的最后一个1。
设x为a1b,a1b中的1位最后一个1,a和b都表示一串01串(当然b全为0或者不存在),如13=1101则a=110,b不存在。
对一个数x取负相当于该数的二进制取反再加1,所以-x = (a^- 1^ b^) = (a^ 0 1...1) +1 = (a^ 1 0...0)。
则 x & (-x) = (a 1 00...) & (a^ 1 00...) = (0...0 1 0...0)。结果就是保留最后个1和后面的0的十进制答案。
如
10进制值二进制值lowbit()值210231114100451011那么 i + lowbit( i ) 可以求出父节点的下标
把子树向右对称翻得出一些空白结点,树变成完全二叉树,看图可知父节点下标和空白结点下标相同
像 i == 5时,父节点下标是6,表示c[5]在c[6]管辖下,c[6]还管了个A[6],c[6] = c[5] + a[6]
void add(int i,int num){ while(i <= n) { c[i] += num; i += lowbit(i); }}给a[ i ]加值,则要把c[ i ] 和 管理c[ i ]的那些 c[ x ] 也加上相同的值,如给1 号元素加1,则要把c[1],c[2],c[4],c[8]都加1。add()代码中 i += lowbit(I)就是父节点下标
那么减值就是参数为负数就行
int getSum(int i){ int sum = 0; while(i > 0) { sum += c[i]; i -= lowbit(i); } return sum;}i - lowbit( i ) 求出跟 另一个c[ x ] ,c[ x ] 管理了 小于 i 的 a [ i ] 中,c[ i ] 所没有管理到的 a[ i ],那么把c [ i ] 和 c [ x ]加起来就是A[1]...A[ i ]的和i12345678i - lowbit(i)00204460如求a[1] ... a[6],那么求c[6] + c[ 6-lowbit(6) ]即 c[6] + c[4]即可
还不过瘾就看看http://www.hawstein.com/posts/binary-indexed-trees.html吧,是Topcoder文章的翻译
这种直接插入求和,修改值的函数( 这里的add() )的循环是从下往上,而求和函数是从上往下,所以也被归类为 向上查询,向下求和的类型
下面来道向下查询,向上求和的题目
HDU 1556 Color the ball
题目很好理解,代码实现不需要a数组。但add()一改就是改一串相关联的,那么要改区间[a,b]元素的值,可以这样:把[1,b]元素值+1,再把[1,a-1]值-1,抵消后就是把[a,b]的元素值+1,因为改的是小于等于 i 的部分值,所以是向下查询
#include <iostream>#include <cstdio>#include <cmath>#include <cstring>#include <cstring>#include <algorithm>#include <cstdlib>#define FOR(i,n) for(int i=0;i<(n);i++)#define ll __int64#define NMAX 100001using namespace std;int c[NMAX],n;int lowbit(int t){ return t&(-t);}void add(int i,int num){ while(i > 0) { c[i] += num; i -= lowbit(i); }}int getSum(int i){ int sum = 0; while(i <= n) { sum += c[i]; i += lowbit(i); } return sum;}int main(){ // freopen("in.txt","r",stdin); int a,b; while(scanf("%d",&n)!=EOF,n) { memset(c,0,sizeof(c)); for(int i=0;i<n;i++) { scanf("%d%d",&a,&b); add(b,1); add(a-1,-1); } printf("%d", getSum(1)); for(int i=2;i<=n;i++) { printf(" %d",getSum(i)); } printf("\n"); } return 0;}
HDU 1166 敌兵布阵 此题还有线段树解法
#include <cstdio>#include <cstring>#define INF 0x7FFFFFFF#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )#define CLR( a , x ) memset ( a , x , sizeof (a) )#define RE freopen("1.in","r",stdin)#define LL(i) (i<<1)#define RR(i) (i<<1|1)#define MAX 50010int ans;int a[MAX];int lowbit(int x){ return x&(-x);}int sum(int i){ int ans = 0; while(i > 0) { ans += a[i]; i -= lowbit(i); } return ans;}void add(int i,int num){ while(i <= MAX) { a[i] += num; i += lowbit(i); }}int main(){ RE; int n,m,l,r,t,x; char cmd[10]; scanf("%d",&t); FOR(tt,1,t) { CLR(a,0); printf("Case %d:\n",tt); scanf("%d",&n); FOR(i,1,n) { scanf("%d",&x); add(i,x); } getchar(); while(scanf("%s",cmd),cmd[0]!='E') { switch(cmd[0]) { case 'Q': scanf("%d%d",&l,&r); ans = sum(r) - sum(l-1); printf("%d\n",ans); break; case 'A': scanf("%d%d",&l,&r); add(l,r); break; case 'S': scanf("%d%d",&l,&r); add(l,-r); break; } } }return 0;}
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- AndroidManifest.xml中声明不同包中的Activity
- 畅通工程续
- VC获取MAC地址的4种方法
- 大数(高精度)加减乘除取模运算
- flash与游戏笔记:Vbs脚本 帮助自己跳转到资源路径
- 树状数组
- [转]和一圈同行聊完APP市场推广,感觉整个人都不好了
- 心学小谈
- 解决CATextLayer,换行和文字不清晰的问题
- Android中的setContentView用法
- 虚拟机-网络
- Android 添加.so文件
- linux内核定时器-比较齐全
- a simple erlang process pool analysis