树状数组讲解(简洁好懂)

树状数组

树状数组是用于维护前缀和的数据结构,支持单点的查询和修改,如果要修改区间的值则需要用到差分数组。数字太大时可以在排序后进行离散化。

树状数组原理详解:

首先我们需要知道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形成了一棵树,我们称其为树状数组。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值