树状数组的概念一开始理解的很吃力(也可能是学姐讲的太快了(逃
于是晚自习好好思考了下(顺便混篇博客发|・ω・`)。
不过讲树状数组的博客到处都是,这里只写一些自己理解并且没在网上看到的的东西。
树状数组的思想是将数组不断两两细分从而用代替
,在二分分治里都有类似的思想。
是分层的依据,也是数组二分的依据,用
来计算。
从图中可以看出,如果i为左子结点,那么父结点的编号是,反之则是
。
事实上这个概念可以进行扩展:
如果i是左结点,那么就是
的父结点。否则取它的父结点,直到当前节点是左结点为止,
即为当前结点的父结点。
对于i是左结点的情况显然成立。下证i为右结点的情况:
考虑到取父结点后,
并且有
那么
由此可以递归的将i为右结点时的转化为其父结点的
,直到
为左结点,那么结果就是
的父结点
对于的情况同理。
有了以上推论为基础,就不难理解树状数组的两个操作了:
辅助数组
在图中我们可以看到就是
结点以及左边的白条,如果我们要求前缀和
,显然
为
以及它左侧的白条的和,并且由于上方的白条包括下方的白条,所以我们只需要向左上方走。结合前面的推论可知向左上方走的操作就是
。这样就不难写出求前缀和的函数sum:
int sum(int x)
{
int ret = 0;
while(x > 0)
{
ret += C[x];
x -= lowbit(x);
}
return ret;
}
同样的,对于另一个操作add也同理,我懒得写了就贴个代码:
void add(int x,int d)
{
while(x <= n)
{
C[x] += d;
x += lowbit(x);
}
}
update:
大白中提到树状数组可以看做线段树去掉所有的右子树。
线段树可以直接求出区间和,但注意到在线段树中右子树的区间和完全可以用父亲的值-左兄弟的值得到,基于这种思路优化得到的就是树状数组。