【模板篇】树状数组们(二)

上一次,我们说到了,树状数组是可以在O(logn)时间复杂度内,支持单点修改、区间求和的数据结构,还具有 常数小代码短 的优势。

你忘了???
好吧,把你传送回去看看:http://blog.csdn.net/enzymii/article/details/54952957

那么为什么时间复杂度是O(logn)呢???
下面来证明一下。。。(其实没必要看的2333)
1)首先来看单点修改……每次加lowbit的时候都会把最后的一个1变为0,一个数最多有logn个1(而一般的数的1远少于logn个,这也是常数小的原因之一),所以复杂度就是愉悦身心的O(logn)。。。。
2)然后来看区间求和……每次减lowbit也会把最后的一个1变为0,一个数最多有logn个1,所以区间求和单次操作也是O(logn)的。。。。

上面的证明没啥用,反正看了也不懂
其实,树状数组如果只能干这点小事,就不用学了,(虽然代码就几行学了也没坏处。。。)(逃)

然而,树状数组很能干哦~~虽然用途比 线段树 小一些,但也能干不少活呢。。

上一次,我们说它能单点修改,区间求和(这里只要满足前缀和性质的都可以)。。
这一次,反了它了,它要区间修改,单点查询!!!

我的天哪,这怎么做???
莫慌莫慌。。。内个,各位都知道差分不?不知道,那先别学树状数组了,我们以后还要和差分打不少交道呢~~
要是不会差分,出门左转去百度学完再来好不好,不然你会一头水的。。。

=============================会差分的分割线=============================

好的,闯到这里的少侠都是会差分了的对不对。。。
我们现在来讲。。
首先,树状数组最基础的功能,就是上次谈到的单点修改,区间查询。。
遇到区间修改时,树状数组就显得有点干不了了_ (:з」∠)_
所以,我们需要化区间为单点,没错,就是差分╮(╯_╰)╭
比如下面的栗子(表格好丑):

a(数组)13689
c(差分)12321

我们要是让区间[2,4]+2,则c2+2,c5-2,差分数组长介个样子:

c1432-1

我们用树状数组维护上面的差分数组,你求一下第n项的前缀和,是不是就成了第n项修改后的值?
对,就是这么神奇!
下面贴代码(这次只有核心代码了哟):

class Binary_Tree2{

    inline int lb(int x){
        return x & -x;
    }

    void add(int x, int i){
        for(; x <= n;x += lb(x))
            c[x] += i;
    }

    int sum(int x){
        int s=0;
        for(; x;x -= lb(x))
            s += c[x];
        return s;
    }
    //上面是不是很熟悉呢,因为树状数组还是那个树状数组,只是用它维护的信息不同了。。
    void adda(int L, int R, int i){
        add(L, i); add(R + 1, -i);
    }
    //差分的加法
}

还记得上次的初始建树么。。
我们只要简单的

add(i,getnum())

就可以了对吧~
但是,这次我们要差分。。
唔,那怎么搞呢,当然,冰雪聪明的你应该已经知道了,
我们可以再加一句。。
像这样就行了:

k=getnum();
add(i,k); add(i+1,-k)

就是这样了,应该是不难的。。(你可以理解为从l到l的区间更新。。)
所以有一种奇怪的做法,比如:

adda(i,i,getnum())

你会发现其实是一样的。。。。。

好了,准备完毕~~

至于查询点i嘛,只需要调用sum(i)就万事大吉啦~(≧▽≦)/~
嗯 就是介样,不难吧~

 

posted @ 2017-02-10 11:12  Enzymii  阅读(118)  评论(0编辑  收藏  举报