树状数组
树状数组是用于维护前缀和的数据结构,支持单点的查询和修改,如果要修改区间的值则需要用到差分数组。数字太大时可以在排序后进行离散化。
树状数组原理详解:
首先我们需要知道lowbit(x)是返回x的最后一个1,例如二进制数10100就返回100.
树状数组的核心思想是将一个数分解成若干零件,在维护的时候,直接对这些零件进行处理,在查询的时候再将这些零件组装成我们想要的数。
我们先来看看树状数组是如何查询的:
ll query(int p)
{
ll ans = 0;
for (; p; p -= lowbit(p))
ans += f[p];
return ans;
}
仔细观察可以发现,查询的过程是将数p进行了二进制分解。
例如:二进制数p为1101,我们就要将它分解为:
1101
1100
1000
分解的方式是减去上一个数的最后一个1
那么,为什么这种分解方式是正确的呢,也就是要证明这些零件包含了p之前前的每一个数,并且值包含一次。
然后我们在观察它的插入过程:
void insert(int p, int k)
{
for (; p <= n; p += lowbit(p))
f[p] += k;
}
可以发现如下规律:
以4位二进制数为例
从0001到1000的数都会被累加到1000当中
从1001到1100的数都会被累加到1100当中
从1101到1110的数都会被累加到1110当中
1111单独加到1111当中
例如0110->1000,0101->0110->1000,1010->1100
这些1000,1100,1110,1111就是所有的零件,所有的前缀和都可以由他们组合得到,并且不重不漏,他们的特点是加上lowbit后会变成10000,也就是他们最终会统合到10000中,所有小于等于10000的数都会被统计到10000中,所以我们也递归地证明了所有小于等于1000的数会统计到1000当中。加上lowbit操作本质上是产生进位,填补lowbit位后的第一个0,然后把这个0后面的位都变成0,而从0001到0111中,第一位都是0,所以在不断加上lowbit后,一定会到达1000这个数,把值传给他。1001到1011也是一样,第二位总是0,在不断加上lowbit后,一定会把第二位变成1,也就是1100。而1000,1100,1110,1111均可视为01000,01100,01110,01111,他们的第一位也总是0,所以一定会累加到10000当中,也就通过lowbit形成了一棵树,我们称其为树状数组。