树状数组整理(3.RMQ问题)

邪道,以下内容纯属娱乐

我们先前的各种基于BIT的应用和变形,都还是围绕BIT维护前缀和展开的,而区间信息则依赖于前缀和(区间和)的良好性质——支持区间减法,所以我们可以把区间拼接起来再做减法
SegT不一样,它维护的信息最后都是通过区间加法整合到一起,所以它可以搞RMQ,BIT只能默默抹泪……想处理RMQ,只能学习SegT用维护的区间信息直接拼出目标区间,用纯区间加法来维护,而不能求前缀

这次,a[i]还是原序列c[i]存的是a[i-lowbit(i)+1..i]中的最小值,查询[l,r]要从r出发逼近左边界l
由于x-lowbit(x)相当于把x最右端的1变成0,不停减去lowbit就会超出左边界,何解?我们可以减1,维护求助于a[],这样又把末尾的一堆0变成了1,lowbit一下子变小,又可以继续减了……
这样我们先设计出查询的方法:

_int rmq(int l,int r) {
  for (s=inf; l<=r; s=min(s,a[r--]))
    for (; r-l>=r&-r; r-=r&-r) s=min(s,c[r]);
  return s;
}

效率是个问题,如果一路-1-1-1-1-1-1那不就是渣死的O(n)查询么?
再看r-1的意义,它把原lowbit(r)的对应位取反了,而r-1的条件是剩余部分小于lowbit(r),故r-1取反后,r末尾那一串1不会不够用,换言之下次-1的时候,被取反部分的位数小于前次。最多有logn位所以最多减去logn次1,而每次减完了1是撑死一重logn,总共是O(logn*log)无递归也还算说的过去

至于修改我个人真没什么好方法,按定义c[p]=min(rmq(p-lowbit(p)+1,p-1),a[p](new))更新,然后再依次更新覆盖a[p]的(这又是老路数):

void change(int p,_int k) {
  for (a[p]=k; p<=n; p+=p&-p) 
    c[p]=min(rmq(p-(p&-p)+1,p-1),a[p]);
}

修改因为嵌套了一个rmq,本身又要定位logn个点,O(logn*logn*log)比较慢了……
总之还是贴个全代码:

struct BIT_rm {
  _int n,a[N+1],c[N+1],s; void init(int s) {for (n=0; n<=s; n++) c[n]=inf; n--;}
  _int rmq(int l,int r) {
    for (s=inf; l<=r; s=min(s,a[r--]))
      for (; r-l>=r&-r; r-=r&-r) s=min(s,c[r]);
    return s;
  }
  void change(int p,_int k) {
    for (a[p]=k; p<=n; p+=p&-p) 
      c[p]=min(rmq(p-(p&-p)+1,p-1),a[p]);
  }
};

实践测试上,时间被线段树爆出了银河系!编码复杂度……自下而上式线段树也表示压力不大,总体来说这个BIT用法纯属娱乐……
另外,求纠错求批评求改进求优化,先谢过Orz!

下期预告:多管闲事的BIT

你可能感兴趣的:(总结)