树状数组总结——转

转自:夏天的风 http://blog.csdn.net/shahdza/article/details/6314818#comments

又做了几道树状数组的题,决定放一块儿总结一下;恩,总结一下。。

(ps:大牛可以直接跳过。。。)

这得从一张图说起;

树状数组中用的d【】,每个点都有一定的管辖范围;

如d[1]=a[1];

d[2]=a[1]+a[2];

d[3]=a[3];

d[4]=a[1]+a[2]+a[3]+a[4];

等等;

这样的结构关键是为了,对一个数组内部动态的删除,增加,来高效的求某个点或者某个区间的值;

比如说对数组a,改变某一位的值需O(1),求某个k区间值O(k);

这样的m次操作是用O(m*k);

显然数据很大时,效率要差很多;

而树状数组,它改变某一位,或者求某个区间的和,都是O(logN);效率大为改善;

对于上图怎么计算区间的和;关键是需要几个函数;

先说树状数组最简单的用法,修改上面图中的某个点,并求某段区间的和;

第一个函数;

int lowbit(int x)
{
    return x&(-x);
}
这个函数主要是用来求的是某个点管辖范围;

x&(-x);这个东西的由来就不说了。。。灰常奇妙啊。。

如果是x+=x&(-x);就是得到的改点的父节点的值;比如x=4时;就能得到8;

而x-=x&(-x)的话,就是得到x这个点的管辖区间的下个区间的管辖点;

比如说,x=7,代入后6;不断循环到0能依次得到 6.。。4.;

把他们所有的管辖区间正好是1....7;

第二个函数
void update(int x,int num)
{
    while(x<=N)
     {
         d[x]+=num;
         x+=lowbit(x);
     }
}

这个函数,是用来修改树状数组的;

如果是一般的算法只用修改改点就可;但是树状数组必须修改所有改点被管辖的区间;

比如把a数组的 a[2]减去1,(令N=16);则所有2被管辖的点有4,8,16都应该减去1;

就是调用函数 update(2,-1);
第三个函数
int getSum(int x)
{
    int s=0;
    while(x>0)
     {
         s+=d[x];
         x-=lowbit(x);
     }
    return s;
}

这个函数就是求区间和了。。比如getSum(7)的话,就是求a[1]+a[2]+...a[7];

上面是最基本的用法;要学习树状数组必须把上面的过程原理搞明白;

搞明白d数组和原来a数组的区别,初学者应该自己画一画;

另外有好几种最原始树状数组的变形(一会儿讨论二维的);

大体上可以分为两种;

一,每次修改的是一个点,所求的是关于某段区间;

这种情况最好办;比如说poj2352 stars;求每个点前面比他小的点的个数;

只用设置数组a[],先全是0,然后有某个点就依次修改,并以此统计;

这一种是最基本的向上修改,向下统计;(基本上都是。。。)

二,每次修改的是一个区间,所求的值是关于某个点的;

代表的典型题目是HOJ1556 color the ball;

这个题是每次修改了一整个区间,最后求的是每个点修改的次数;

这个需要将上面的函数,稍加修改;

void update(int x,int num)
{
    while(x>0)
     {
         d[x]+=num;
         x-=lowbit(x);
     }
}

要向下修改,将它后面的区间都加一遍;

再向后修改,把不必要的修改区间再减去;

用他的父节点记录每个点的染色次数;

统计时要

int getSum(int x)
{
    int s=0;
    while(x<=N)
     {
         s+=d[x];
         x+=lowbit(x);
     }
    return s;
}

这种修改一个区间,而求某个点的修改次数的,一般的都是向下修改,向上统计;

对于二维的情况,d[][]中的d[i][j]这个点也有他自己的管辖区间,从一维推广到二维;可以很容易理解;

一维情况是一段区间的管辖范围,二维就是一个矩形的管辖范围,而每一维可以独立考虑;

*****************************************************************************

下面是几道题目的简单分析;

一,两种情况;
1,要向上统计,向下修改;一般是修改一段区间的值,查找的是某个位上的值;

HDU1556 color the ball;

这个题是这类题最基本的;关键是理解这个向上统计,向下修改时怎么操作的和原理;

poj2155 Matrix

这个是楼天成出的题目;是一道很典型的二维数状数组的应用;是hdu1556的二维版本;

记住要向上统计,向下修改;二维情况,最主要的是理解那个数组中的每个点保存的值的意义(一个矩形区域的总和);最后记住取余;

poj2299 Ultra-QuickSort;(求逆序数);

这个题可以用归并排序做出,这里就略过不说了。。

求逆序数,也是树状数组的典型应用,求一堆数中前面比他大的数的个数;也需要向上统计,向下修改;

可以与求一串数中前面比他小的数的个数这个题来比较思考。。。

poj3067 Japan

此题要先对x排序,再对y求那个前面比他大的数的个数;(理解下);

也是基本应用;

**************************************************************************************************************************

二,2,要向上修改,向下统计;一般是修改某个位置上的值,查找的是一段区间的和;

poj2352 stars

这个题是最基本的一维情况,刚才已说过。。

poj1195 Mobile phone

是个二维的情况。改变的是某个位置上的数的大小;就将所有管辖这个点的 点修改;

统计的时候,就是向下统计;

poj2481 Cows

这个题要把y按不降排序,x不升排序,基本是就是poj2352了。。不过注意其中可以有完全重合区间;

poj3321 apple Tree;

这题要用到树状数组;但难度在那个dfs怎么求那个时间戳;求出后,在套入树状数组的那部分东东。就搞定了。。

poj1990 MooFest ;

这题O(n^2)算法必然超时;排序后要存储前面比他小的数的总和,和个数;故树状数组;

poj2309

树状数组的小试牛刀,一下搞定,想明白的话。。

可以用树状数组的地方,一定可以用线段树;反过来则不行;

但是,树状数组编码简单,对于一定区间修改,求值,很高效;

另外,一个讲树状数组灰常好的网址要吐血推荐。。。

http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=binaryIndexedTrees#prob

================================================================================================

树状数组,刚开始学习的时候觉得很神奇,特别是求lowbit的时候...写过几题之后,觉得这东西还是挺好用的。需要注意一个超时陷阱:x==0的情况

树状数组在动态求区间之和的问题时候是一个很好的选择。给出几个例题:

(1):http://acm.pku.edu.cn/JudgeOnline/problem?id=2352 starts

题目大意为在二维坐标上给出一些星星的坐标,求某一个星星左方,下方,左下方的星星个数。求解这题时学习到了 “降维” 的思想,首先把星星按照Y坐标从小到大,X从小到大排序。树状数组以X坐标建立。这样,在每次对一个星星进行统计时,之前出现过的星星,只要X坐标比其小,则必在其左,下,左下方。 这时,只需利用树状数组进行求从1~x的和既可,统计完之后要记得更新树状数组(后面附上代码) (注意坐标为0的情况,这里是一个超时陷阱)

(2):http://acm.pku.edu.cn/JudgeOnline/problem?id=2155 Matrix

二维树状数组,题目大意为一个二维矩形平面,里面有数字0或1,初始时全为0,给出两种操作,一种是改变一段子区间内的数值,把0改为1或者1改成0。另外一种是查询某一坐标上的数值是0还是1,。

数据范围:坐标N<=1000,操作数T <=50000。

解此题的方法很巧妙,首先我们考虑如果改变区间内数值时,把该区间内的每一个点都改变一次,那么时间一次操作就需要n^2,这样太耗时。想象一下,如果我们在求某一点的操作次数时,把求操作次数转化为求该点到(1,1)这点的和,那么我们在对某区间操作时,我们可设立四个哨兵来标记:

如图,当我们要改变区间(3,3)~(6,5)的数值时,分别在按照上图所示改变矩阵的数值,这时,区间内部到(1,1)点的和为1,而区间外部到(1,1)点的和为0,这样就吧问题转换为了动态求二维区间和的问题
,这时就用二维树状数组来解决既可

(3):http://acm.pku.edu.cn/JudgeOnline/problem?id=3321

题目大意为给出一棵树,节点数<=100000,树中的节点可能有苹果,两种操作:一种是改变某一节点的苹果,有改为无,无改为有。第二种操作是求某一节点和以该节点为根的子树的苹果总数。操作数达到了100000;

这题需要把一个树形结构变换为线性结构,然后使用树状数组动态求和。变换方法为DFS一次这棵树,记录下每个节点第一次访问和最后一次访问时的顺序编号,这时,在某节点第一次访问和最后一次访问编号之间的访问编号,必定为该节点的子节点,按照访问编号建立树状数组,当改变某一节点苹果时,只需按照该节点第一次访问的编号在树状数组中修改值,查询时统计某节点在第一次访问和最后一次访问编号之间的和。

树状数组总结——转

时间: 2024-08-02 03:46:54

树状数组总结——转的相关文章

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

(POJ 3067) Japan (慢慢熟悉的树状数组)

Japan Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 29295   Accepted: 7902 Description Japan plans to welcome the ACM ICPC World Finals and a lot of roads must be built for the venue. Japan is tall island with N cities on the East coas

【二维树状数组】See you~

https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/F [题意] 给定一个矩阵,每个格子的初始值为1.现在可以对矩阵有四种操作: A x y n1 :给格点(x,y)的值加n1 D x y n1: 给格点(x,y)的值减n1,如果现在格点的值不够n1,把格点置0 M x1 y1 x2 y2:(x1,y1)移动给(x2,y2)n1个 S x1 y1 x2 y2 查询子矩阵的和 [思路] 当然是二维树状数组 但是一定要注意:lowbi

Vijos P1066 弱弱的战壕【多解,线段树,暴力,树状数组】

弱弱的战壕 描述 永恒和mx正在玩一个即时战略游戏,名字嘛~~~~~~恕本人记性不好,忘了-_-b. mx在他的基地附近建立了n个战壕,每个战壕都是一个独立的作战单位,射程可以达到无限(“mx不赢定了?!?”永恒[email protected][email protected]). 但是,战壕有一个弱点,就是只能攻击它的左下方,说白了就是横纵坐标都不大于它的点(mx:“我的战壕为什么这么菜”ToT).这样,永恒就可以从别的地方进攻摧毁战壕,从而消灭mx的部队. 战壕都有一个保护范围,同它的攻击

CF 313 DIV2 B 树状数组

http://codeforces.com/contest/313/problem/B 题目大意 给一个区间,问你这个区间里面有几个连续相同的字符. 思路: 表示个人用树状数组来写的...了解了树状数组的本质就行了. 当然用sum[r]-sum[l]也是可以的

Hdu5032 极角排序+树状数组

题目链接 思路:参考了题解.对询问进行极角排序,然后用树状数组维护一下前缀和即可. /* ID: onlyazh1 LANG: C++ TASK: test */ #include<bits/stdc++.h> using namespace std; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long ll; const int maxn=1010; const int maxm=10

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

【初识——树状数组】 区间求最值

说树状数组其实是一个索引表,但是是一个特殊的,树状的索引表,它利用了二进制的一些特性. 就区间求和的要求来说: 首先我们用a[]数组来存储原始数据.然后在a[]之上构造c[]数组来作为树状数组. 如图 这个图表示,当i为奇数时,c[i]中保存的都是a[i]本身.然后,c[2]中保存了a[1], a[2],共2个,c[4]中保存的是a[1], a[2], a[3], a[4],c[6]又是保存两个,c[5]和c[6].c[8]保存8个,c[1], c[2], c[3], c[4], c[5], c

树状数组求区间最大值

------  一直用 线段树 求区间最大值,想换种思路,用树状数组试试,肯定是可以的. 首先要对 树状数组的每个 i 所管理的区间有一定的理解.详见上篇博客: 树状数组(BIT)

HOJ1867 经理的烦恼【树状数组】

题目链接: http://acm.hit.edu.cn/hoj/problem/view?id=1867 题目大意: 有C家连锁店,编号1~C,有N条指令,每家店初始的商品数目都是M.接下来N行是命令. 命令0:0 x w,连锁店x的商品数量变化为w,w > 0商品数量增加,w < 0商品数量减少. 命令1:1 x y,询问编号区间为[x,y]的连锁店商品为素数的商店有多少家. 思路: 因为区间比较大,所以用树状数组来做.用一个数组Shop[]来存放每家店的商品数目,Tree[] 表示树状数组