树状数组(BIT)

关于树状数组的详细介绍,参见博客
作者:Hawstein
出处:http://hawstein.com/posts/binary-indexed-trees.html
声明:本文采用以下协议进行授权: 自由转载-非商用-非衍生-保持署名|Creative Commons BY-NC-ND 3.0 ,转载请注明作者及出处。

这里仅给出树状数组相关操作的代码

sum

int sum(int idx)
{
    int sum = 0;
    while(idx>0)
    {
        sum+=tree[idx];
        idx-=lowbit(idx);
    }
    return sum;
}

点修改

void add(int idx,int val)
{
    while(idx<=n)
    {
        tree[idx]+=val;
        idx+=lowbit(idx);
    }
}

个人经验

1、这种树状数组一般用在查询区间比较频繁的地方。
http://acm.split.hdu.edu.cn/showproblem.php?pid=1166
2、关于树状数组的建模。
通常能把问题转化为前缀和或者区间和的形式,能够出现动态的点修改。
http://acm.split.hdu.edu.cn/showproblem.php?pid=1556
比如这个问题HDU 1556,如果直接求第i个点的操作次数,显然是难实现的,
如果对于修改的区间把他转化为,f[a]+1,而sum[i] = ik=1f[k] ,来表示点 i 的操作次数,显然对于把 [a,b] 中的所有点加1就相当于把f[a]+1,而这时对于那些 i>b 的点显然值不应该加1,所以对应的区间操作就转化为

add(a,1);
add(b+1,-1);

而所求的结果就转化为sum(i)
把复杂度从原来的 O(ba) 降为 O(logN)
练习
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2330

常见应用

i<jai<aj

求这种在前缀区间里比比 ai 值小的元素的个数和,或者和是很常见的应用,
就计算个数和来说吧,可以用x[v]表示值为v的元素个数,那么对于每一个 ai 来说树状数组(这里的树状数组是对值做的)中 sum(ai1) ,就是所求的值,不过因为求的是 i 之前的元素中比他小的个数,所以必须在刚好扫描到第 i 个元素后计算 sum
练习poj 1990 MooFest
ni=jnj=2max{v(i),v(j)}abs(ji) n20000 ,
根据 vi 排序,对于每一个 vi 需计算的值为 v(i)j1i=0abs(ji) ,这里 i,j 是其对应的坐标,分别计算比j小的前缀和与比他大的就行了

你可能感兴趣的:(算法理论)