树状数组
树状数组
简记
1.快速求前缀和 O(logn)
2.修改某一个数 O(logn)
基于二进制的想法
对于一个序列 a[1]~a[n],树状数组用于维护其前缀和。支持logn复杂度求前缀和与logn复杂度修改序列中的某一个元素。
设我们有一个长度为n的序列,其中:
我们将此序列按照二进制的思想可以分成k段(k=n二进制表示下1的个数):
如图,为tree[]为每一段绿色区间之和:
图中任意一个区间都能由之前若干个区间覆盖,而且覆盖区间的数量正好是当前区间二进制表示下1的个数,而且每次都递减最后一位1。因此我们可以借助lowbit运算来求得每一个构成的子区间:
1 | int lowbit(int x){ |
建树方式:
在已知数组a[]的情况下有三种建树方式:
1.最简单的O(nlogn)
1 | for(int i=1;i<=n;i++){ |
2.O(n)建树法:
1 | for(int i=1;i<=n;i++){ |
3.回归本质建树法O(kn)
1 | //1.先预处理a[i]的前缀和 |
查询操作:
以c[16]为例:
因此我们可以总结出查询操作的模板:
1 | int ask(int x){ |
修改操作
我们逆向查询操作的步骤,当修改某一个数的值的时候,会逆向影响到由此数组成的所有查询路线,因此当修改一个值后,此数的所有查询路线对应的数都需要修改:
1 | int add(int x,int c){ //在x位置插入c |
大致题型:
适用于针对一个区间的修改或者查询操作