『数据结构』树状数组
树状数组的问题模型:现在有一个这样的问题: 有一个数组\(a\),下标从\(0\)到\(n-1\),现在你要进行\(w\)次修改,\(q\)次查询。 \(for\) \(example\),\(x=6\),它的二进制为\(110\), 求负数的补码的简便方法: 先把这个数的二进制写出来,然后从右向左找到第一个\(1\),这个\(1\)不要动和这个\(1\)右边的二进制不变,左边的二进制依次取反,这样就求出的一个数的补码,说这个方法主要是让我们理解一个负数的补码在二进制上的特征,然后我们把这个负数对应的正数与该负数与运算一下,由于这个\(1\)的左边的二进制与正数的原码对应的部分是相反的,所以相与一定都为\(0\),由于这个\(1\)和这个\(1\)右边的二进制都是不变的,因此,相与后还是原来的样子, 所以,这个得出的结果就是\(lowbit(x)\)的结果。 lowbit函数:int lowbit(x) { return x & -x; } 二进制的视角:一个数\(n\),假设\(n=6\),它的二进制为\(110\),我们把它表示成累加的形式\(110=100+10\),这样是可以的,那么我们要求前\(6(110)\)项的和可以这样求: \(∑i=16=(a[1]+a[2]+a[3]+a[4])+(a[5]+a[6])\) 注意括号中的元素个数, \(∑6i=1=(a[1]+a[2]+a[3]+a[4]+(a[5]+a[6])=\) \(∑6i=1=(a[1]+a[2]+a[3]+a[4])+c[6]\) \(∑i=16=(a[1]+a[2]+a[3]+a[4])+(a[5]+a[6])=\) \(∑i=16=(a[1]+a[2]+a[3]+a[4])+c[6];\) 你可以把\(c[6]\)去掉,就变成了 \(∑4i=1=(a[1]+a[2]+a[3]+a[4])\) \(∑i=14=(a[1]+a[2]+a[3]+a[4])\) 这个区间就靠右端点了, \(∑4i=1=c[4]=c[6-lowbit(6)]\) \(∑i=14=c[4]=c[6-lowbit(6)]\)。 设计一种数据结构,需要的操作无非就是更改和查询, 查询这里说的查询是查询任一区间的和,由于区间和具有可加减性,所以转化为求前缀和; 修改修改某一位置上的元素的时间复杂度为\(O(1)\), 图片中已经把\(c\)数组的后缀和这个含义已经表达得很清楚了。 树状数组的代码实现对某个元素进行加法操作:void update(int x) { while(x<=n) { c[i]+=x; x+=lowbit(x); } } 查询前缀和:int sum(int x) { int res=0; while (x>0) { res+=c[x]; x-=lowbit(x); } return res; } 洛谷树状数组题: 【模板】树状数组 1:p3374 【模板】树状数组 2:p3368 中位数:p1168 逆序对:p1908 虔诚的墓主人:p2154 无尽的生命:p2448 推销员:p2672 上帝造题的七分钟:p4514 (编辑:ASP站长网) |