树状数组の整理

在处理RMQ问题的方法中我最不熟练并且无力的就是树状数组,因为只要碰到这种问题基本上都打了线段树。——线段树都会了要树状数组干嘛,树状数组能求区间最大嘛?树状数组能区间修改嘛?树状数组能持久化嘛?——然后这些都是可以的....给被我冤屈的树状数组正个名吧...

树状数组的存储方式:

  树状数组可以看做...用二进制末个1的位置分层的线段树?...爱线段树爱得太深,所以什么都以线段树为基准...

  说到树状数组就不能不放这张图,老人家书上的树状数组我觉得更加清晰,但是拿不下来...这就不能怪我了

  因为二进制的性质太好了,就像倍增一样我们每隔一段就可以"腾出"一个点来充当两个点父亲----末尾1的位数往前进一.

  我们让每个点存它直到左儿子lowbit=1的和.

  我记得我是看过整个树状数组的体系的证明的,但是我已经忘光了(雾

区间求和:

  在树状数组的结构基础之上,我们可以很方便地求出前缀和,如果我们要求1-n的前缀和则从n开始一直减去lowbit并且加起来即可.相当于从那个点一直往左上爬,边爬边加上当前点的值.

  为什么是正确的呢,首先当前点左上方的点记录的和一定不包括当前点-----毕竟维护的是前缀和,如何证明它覆盖了整个区间呢.     

  不管了!反正就是这样的!!!证明一遍太麻烦!!

  那么我们要求一段区间的和就只要求两段减一减就好了.

单点修改:

  跟区间求和相反,单点修改时更新数据要往右上走,因为右上的顶点才是包含它的.

  我们只要走到根,边走边改就好了.

区间修改(加加减减):

  类似求和的思想,修改l,r我们把1,r加上a.    1,l-1减去a就好了---------不过还是要一个一个点改.

区间最值:

  这个区间最值有点像平衡树搞最值-----旋出一个完全只有那段序列的子树,然后更改即可.

  我们注意到,一个点与它父亲结点之间的点维护的是这个序列,我们在更新的时候只要更新他们即可.因为这个点的减去它的lowbit是它的父亲,我们只要在这个范围之内遍历结点即可.

  修改的代码:

  

void change(int r) {
    c[r]=num[r];
    for(int i=1; i<lowbit(r); i<<=1)
        c[r]=max(c[r], c[r-i]);
}

  求值时同理啊.一模一样,不断找父亲(来自ioi大爷的代码)

  

int getk(int l, int r) {
  int ret=num[r];
  while(l<=r) {
    ret=max(ret, num[r]);
    for(--r; r-l>=lowbit(r); r-=lowbit(r))
      ret=max(ret, c[r]);
  }
  return ret;
}

先这样吧,据说可以可持久化,不过我觉得也没卵用啊...把这个可持久化还不如搞线段树...

  

Sometimes it s the very people who no one imagines anything of. who do the things that no one can imagine.
原文地址:https://www.cnblogs.com/YCuangWhen/p/5210833.html