树状数组进阶(区间修改+单点查询)

  这篇文章既然是进阶的文章,那么肯定需要一定的基础知识,所以,如果您对树状数组的基本原理和基本操作(区间查询和单点修改)不熟悉的话,请先看看我的另一片文章:树状数组趣解,因为有些基本的内容,我在这里就不会再提了。
  我们来看看本次要讲的内容和树状数组的基本职能有什么关系,一个是“区间修改+单点查询”,另一个是“区间查询+单点修改”,有什么发现?
  两者正好相反,所以我们也可以换一下思路,将两种操作的下标变换方向换一下,我先给出代码,再给原理。如果您善于思考,看一下就懂了,您也就不必再花时间看我的粗鄙的解释,当然了,如果您乐意,看看也无妨。

代码

1、前几项值的修改

void change(int x,int p){//将1到x全加上p
    while(x>0){
        C[x]+=p;
        x-=lowbit(x);
    }
}

我们把这段代码和基本操作的点修改对比一下就能看出差距了,点修改是x+=lowbit(x)。

2、区间修改

void update(int l,int r,int p){//[l,r]上每个数加上p
    change(r,p);
    change(l-1,-p);
}

3、点查询

int query(int x){
    int ans=0;
    while(x<=n){
        ans+=C[x];
        x+=lowbit(x);
    }
    return ans;
}

原理

  下面是我讲解的时间了!这种操作应该怎么理解?我们从它的本质上去看。
  我们先来看看区间修改做了些什么。其实它只是将要加的值分配到对应的大结点,并不向小结点下放,比如:
又是这张熟悉的图
又是这张熟悉的图,比如我们要对1到8进行修改,那么它只会干一件事,那就是把C[8]修改然后就不再改动,因为8-lowbit(8)=0。
  那么,为什么可以这样做?这还得结合我们得点查询操作,毕竟我们这一步是为了单点查询服务的。所以我们来看看查询操作都干了些什么,很显然,它顺着它的父节点,把每个父节点的值都加了起来直到结束。那么我们还是来看上面这个例子,C[8]修改过后,1到8内的每一个位置在被查询的时候,总会加到C[8]这个父节点,这样就成功地把修改体现在查询中了。
  或者我们还能换个玄虚的解释,那就是把点当区间分解去修改,把区间合成就得到了点(查询)。放在这里供大家娱乐。

结束语

  挺简单的是吧?记得总结复习!

猜你喜欢

转载自blog.csdn.net/cggwz/article/details/78380806