树状数组是一个优美小巧的数据结构,在很多时候可以代替线段树。树状数组的本质就是一种通过二进制位来维护一个序列前i和的数据结构。(详见论文或http://old.blog.edu.cn/user3/Newpoo/archives/2007/1712628.shtml)
树状数组按照维数分为1维和2维,按照类型分可以分为插点问段和插段问点,这两个互逆,只是改变for循环的方向就可以了。一般以插点问段较为常见,对于查点操作,只需查询前n项和及前n-1项和,便可得到第n项的值
插段问点转化为插点问段:
当要查询A[i]的时候,我们只需在数组B上使用一般的树状数组操作查询区间[1,i]即可
当我们要给区间A[a~b]加上delta时,只需要在数组B上对B[a]进行一般树状数组的更新delta的操作,同时对数组B上对B[b+1]进行 - delta操作。
final int lowbit(int k){ return k&(-k);}//可查询0,不可更新0,闭区间
int ss[]=new int[100010],N;
final void inc(int k,int m){
while(k<=N)
{
ss[k]+=m;
k+=lowbit(k);
}
}
final int get(int k){
int sum=0;
while(k>0){
sum+=ss[k];
k-=lowbit(k);
}
return sum;
}
树状数组用做统计很好用,假如y = f(x),事先应该考虑好是把y作为下标,还是x作为下标。
--------------------------------------------------------------------------------------------------------------------------------------------
poj 2352 STARS
题意:给定一组点,问二维空间中各个点的等级是多少,点的等级定义如下:其左下方点的个数。
解法:由于点时按纵坐标从小到大给出的,因此只需要知道前面输入的点在当前点左侧的点有多少个,这个点的等级就是几。一维树状数组插点问段即可实现。
poj 3067 Japan
题意:日本东海岸有n个城市,西海岸有m个城市,现在要修建k条高铁连接东海岸的城市s和西海岸的城市t。问这k条高铁总共有多少个交点。
解法:首先对k条公路按照起点从小到大排序,对于第i条公路,如果前i-1条公路有x条的终点大于t,则第i条公路与前面的x条公路有交点,用树状数组查询前t项和,并用i减之即可达到结果。
poj 3321 Apple Tree
题意:一棵具有n个节点的树,一开始,每个节点上都有一个苹果。现在给出m组动态的操作:(C,i)是摘掉第i个节点上面的苹果(若苹果不存在,则为加上一个苹果),(Q,i)是查询以第i个节点为根的子树有几个苹果(包括第i个节点)。
解法:关键在于树形结构和线性结构的转换,用线性结构中的前i项和代表树形结构中的以i为节点的子树。使用后序遍历为所有节点编号,这样就保证了跟节点的 编号比所有子节点都大,同时记录没课子树最左边的节点号,这样子树的苹果数就等于get(no)-get(min[no]-1);
poj 1195 Mobile Phone
题意:二维树状数组,更新点,查询区间和;
poj 2155 Matrix
题意:给定一个n*n的0-1矩阵,执行一些如下的两种操作:C a,b,c,d :将矩阵中(x1,y1)到(x2,y2)的格子中的0-1进行“非”操作;Q a b :询问当前(a,b)位置的元素的值。
解法:使用二维树状数组,对于每一条更新,更新(x2,y2);(x1-1,y1-1);(x1-1,y2);(x2,y1-1);更新时对每个节点的值进行取反操作。
POJ 2182 Lost Cows
题意:有N头牛,每头牛有唯一的编号(1-N),先给出眉头牛前面有多少头牛的编号小于自己的编号,要求求出所以牛的编号。
解法:对于最后一头牛,前面有x头小于它的,它的编号就是x+1,每头牛已知了后面牛的编号及前面小于它编号的牛的数量,因此从后往前查询每头牛,二分查找确定该头牛的编号(及满足sum[k]+x<k的最小的k),并更新第k个节点的sum值。
POJ 2828 Buy Tickets
题意:卖票插队问题,有 N 个插队行为,告知每次要插到第 i 个位置上,问最后它们的顺序是什么。
解法:与上题类同,思维很巧妙,倒过来做的话就能确定此人所在的位置。
POJ 1990 MooFest
题意:有n头牛,排列成一条直线(不会在同一个点),给出每头牛在直线上的坐标x。另外,每头牛还有一个自己的声调v,如果两头牛(i和j)之间想要沟通的话,它们必须用同个音调max(v[i],v[j]),沟通起来消耗的能量为:max(v[i],v[j]) * 它们之间的距离。问要使所有的牛之间都能沟通(两两之间),总共需要消耗多少能量。
解法:按v从小到大排序,则对于每一只牛a,所有(a,b)的消耗等于v[a]*距离和,计算某点到各点的距离和时,在左侧的点和在右侧的点要分开考虑,因此使用两个树状数组分别统计小于当前x的牛的个数ln和距离和ls,且已知当前所有牛的个数及距离和,
ans += ((cc[i].x * ln - ls) + (sum - ls - cc[i].x * (i - ln)))* cc[i].v;
然后分别更新个数及距离的树状数组。
HDU 2492 Ping Pong
题意:有n名水平不相同的乒乓球运动员,要进行若干场热身赛,每场热身赛需要一名队员做裁判,并要求裁判的水平要介于两名运动员之间,且裁判要住在两名比赛队员之间(第i名运动员的住址编号为i),为最多可举办多少场不完全相同的比赛。
解法:对于每一名运动员,他作为裁判能举办的比赛去却与他左边水平低于他的人数和右边水平高于他的人数,及左边高于他右侧低于他的人数,因此可以用两个树状数组分别维护所有运动员水平小于x的人数及当前水平小于x的人数,有次可知两侧大于x和小于x的人数,遍历每个裁判,累加得到结果。
poj 2481 Cows
题意:FJ有n头牛(编号为1~n),每一头牛都有一个测验值[S, E],如果对于牛i和牛j来说,它们的测验值满足下面的条件则证明牛i比牛j强壮:Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj。现在已知每一头牛的测验值,要求输出每头牛有几头牛比其强壮。
解法:与Stars一题类似,对于这种有两个关键字并且具备单调性的问题,使用树状数组优化的一般方法是固定一个关键字(或者区间的端点),然后查询另一个关键字前n项和的性质确定当前对象的答案。
此题先按s由小到大排序,如果s相同根据t由大到小排序(使得前面的影响后面的)使用树状数组查询当前大于e-1的个数,还要注意特殊处理一下完全相同的区间。