树状数组能在logn时间内动态解决,单点修改,区间求和等一系列问题,当加入差分时还能解决区间修改,求单点实值等问题。
那么树状数组为什么可以这样做?以及如何使用树状数组?
首先我们要搞清楚,树状数组记录的是什么,我们不要将树状数组的内容与寻常数组内容记录的东西搞混淆了这是大忌我们将树状数组内容设为t[]他并不代表每个单位的数值,这也就表明了,树状数组只能解决单点修改,区间求和(当不扩展差分时)而不是求出单点的值,我们一般是不求出单点的具体值的,我们使用的是一个集合,是一个集合囊括可几个数的和!
下面我们讲解何为查询,如何查询
我们发现任何一个数可以将其用二进制分成n个1,这将意味着任意一个数字都可以变为其他几个数之和;我们将任一个数x将其分解一段区域,首先找到x的最后一个1,然后长度即是这个1的大小,形成(x-长度,x]这个形式,列入以15为例,我们可以发现二进制表示为1111则最后一个即使(14,15],(12,14]注意是左开右闭,很明显所有均能肯定能表示这种情况的数组,很明显,对于多个这样的数都会有重复的,那为何不将其整合一下呢,我们求出所有的数字然后仅求出一个(x-长度,x]这种形式的,列入对于15,我们只求第一个也就是其本身,后面的均不在求出,那么我们将其设置为t[]数组,我们发现对于每一个我们都可以通过减一(减去本身的值)然后继续按照这种方式,我们可以发现后面的都可以通过前面概括住从1~x,若我们通过这种方法,不就能通过log时间内求出前缀和吗,按照上文说到后面可以通过前面求出,那么我们将后面的理解为父节点,前面的理解为子节点,那么对于每个父节点x(相对来说),我们都可以通过向下寻找来概括住1~x,这个操作我们叫其为查询。
何为单点修改,如何修改能不重复不遗漏
而对于单点修改,我们知道对于每个t数组其元素并不代表每个数的值,不能随便进行更改,我们发现,当我们修改t[8]时,t[8]会直接影响t[16]这说明当我们用到16的时候是不会再用到8的,那么我们更改t[8],对于那些后面的无法产生影响,所以我们按照这种思路,所有子节点更改仅对父节点进行更改,而对于旁边的节点,当我们进行询问时因为若所求前缀和的x大于我们修改的这个子节点而小于父节点,那么我们对子节点和旁边的节点都会用到,那么不久累加重复了吗,所以子节点改变,影响且仅影响父节点。(你可以理解成对于更大的集合(数字)用到了父节点导致用不到他自己,会导致缺失所以需要将父节点也一并改变)但是如何从子节点找到父节点呢,我们发现父节点找子节点是通过减去自身的1得来的那么它将会使得最后一个1后面的零均变成1(若最后一个1有零的话)列如1000他的子节点有0111,0110,0100,(当然这里指的是区间),那么对于任意一个01串,我们从后面找到连续的1串,而这个前面的那个零改为1时就是这个子节点的父节点,列入子节点是01100,那么第一个0就是连续1前面的零将其改为1后面全变成零即是父节点即10000(我们可以看出将会有多个子节点有着相同的父节点),这与我们通过父节点分裂子节点道理是相符合的。这个操作我们叫它修改。
这样我们使用y总视频的两个操作就能实现上述两个修改与查询,也就是实现了树状数组。