【模板篇】树状数组们(二)
上一次,我们说到了,树状数组是可以在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(数组) | 1 | 3 | 6 | 8 | 9 |
---|
c(差分) | 1 | 2 | 3 | 2 | 1 |
---|
我们要是让区间[2,4]+2,则c2+2,c5-2,差分数组长介个样子:
c | 1 | 4 | 3 | 2 | -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)就万事大吉啦~(≧▽≦)/~
嗯 就是介样,不难吧~