Fenwick Tree

来源:互联网 发布:pk10助赢软件cpzyrj 编辑:程序博客网 时间:2024/05/29 05:57

Fenwick树,俗称树状数组

也就是二叉索引树(Binary Indexed Tree,BIT)

它的作用是什么呢

支持快速区间信息的维护和查询

为什么说是快速,因为它真的很快
O(nlog n)预处理
O(log n)维护和更新

先来看一个问题

给定一个n个元素的数组A
我们进行下面两个操作
1 add(x,d): 让A[x]增加d
2 query(l,r): 计算A[l]+A[l+1]+…+A[r]

怎么办

计算前缀和S,这可以在O(1)的时间解决query。但一旦进行add之后前缀后要重新算过。
不难发现如果进行add(x,d),对前面的前缀和是没有影响的,所以只要更新x之后的就好了
但其实还是不行,以为每次更新的花费还是很大
为什么花费会大
因为一个S的信息存储太大了,所以我们改小一点
但也不能太小,否则和直接算就一样了
所以我们用一个新的东西来存

下面来看一个图

注意到这里的C和我们之前的S很像
那我们就这样处理
用C来记一个和,但不是前缀和,而是类似与前缀和

仔细看这个图,如果我们要求前缀和,从C一直向右走,把走到的所有C加起来就是S

query解决了

再来看add

再看这个图,如果我们要更新一个点,从C一直向左走,把走到的所有C都加d就行了

那我们应该怎么走呢

先介绍一个东西lowbit
lowbit(x)的意思就是把x变为2进制
然后将2进制最右边的1的左边截去所得到的值
举一个例子
lowbit(14)
14的二进制是1110
然后将2进制最右边的1的左边截去
得到10
所以lowbit(14)=2

在计算时我们用下面的代码

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

如果我们已经走到了i点
要向左走就到 i+lowbit(i)
要想右走就到 i -lowbit(i)
ps:如果想不通的话画一下就明白了

然后我们来实现这两个操作
add(x,d)

void add(int x,int d){    int cur=x;    while(cur<=n){        C[cur]+=d;        cur+=lowbit(cur);    }}

然后再实现求前缀和

int sum(int pos){    int s=0;    int cur=pos;    while(cur>0){        s+=C[cur];        cur-=lowbit(cur);    }return s;}

在实现query就很容易了

int query(int l,int r){    return sum(r)-sum(l-1);}

如何初始化呢,add(i,A[i])就行呢

最后我们来分析一下复杂度,add和query都是O(log n)
因为每次都让规模减小了半
而预处理的复杂度是O(nlog n)

于是我们可以用O(nlog n)来预处理
O(log n)来维护和更新
用这样的规模来解决这个问题

ps:当然还可以用线段树,不过在这个问题上BIT已经够用了
pps:注意下标

原创粉丝点击