关于树状数组的一些讨论

树状数组用于在log(n)的时间复杂度修改与询问前缀
相比线段树更好写 常数更小 不过局限性很大 不能用于维护最大最小值之类的情况
最常用的应用(我用过的)大概有:
单点修改区间查询
区间修改单点查询
区间修改区间查询
离散化权值求逆序对

let’s begin

这里写图片描述这里写图片描述这里写图片描述这里写图片描述

以上内容转自洛谷金秋讲义

上面已经把树状数组定义以及修改查询方法讲的很细致了

值得特别提出来的是lowbit的证明部分:
设A’为A的二进制反码,i的二进制表示成xx1yy,其中yy为全0序列,xx可以忽略(因为对lowbit没有贡献最后都会归零) 那么-i = x’x’0 y’y’+1(计算机中负数的存储方式 反码+1) 由于yy是全0 序列 那么反码y’y’就是全1序列 再加上1 那就又变成了全0序列 首位进1 那么-i=x’x’1yy 所以呢 i&-i=xx1yy&x’x’1yy=1yy 即2^k的值(k是i二进制末尾0的个数)

接下来是基本代码:

int lowbit(int x){
    return x&(-x);
}
void add(ll c[],int x,int k)
{
    while(x<=n)
    {
        c[x]+=k;
        x+=lowbit(x);
    }
}
ll query(ll c[],int x)
{
    ll ans=0;
    while(x)
    {
        ans+=c[x];
        x-=lowbit(x);
    }
    return ans;
}

有了上面的代码就可以:
单点修改区间查询:
反正能够log(n)修改,
log(n)查询前缀和,
减一减就是了

关于逆序对:
离散化之后倒序加入每次统计前缀和(后边比当前小的数的个数)

区间修改单点查询:
这个需要用树状数组维护一个记录每个点修改情况的差分数组delta,在修改区间(l,r)的时候只需要修改r+1 和 l
查询x时输出a[x]+sum[x]就可以

区间修改区间查询:
这个比较麻烦了
需要推一推式子
上面区间修改时利用了差分数组delta
即 a’[ x ] = a[ x ]+delta[ 1 ]+delta[ 2 ] … + delta[ x ]
区间查询时用到了前缀和数组
那么我们可不可以综合起来用来实现区间修改区间查询呢
当然了!
来看下面
a’[ x ] = a[ x ]+delta[ 1 ]+delta[ 2 ] … + delta[ x ]
a’[ x-1 ] = a[ x-1 ]+delta[ 1 ]+ … + delta[ x-1 ]
······
a’[ 1 ] = a[ 1 ]+delta[ 1 ]

==>sum’[ x ] = sum [ x ] + x*delta[ 1 ] +(x-1) * delta[ 2 ]+..+1 * delta[ x ]

观察右边delta类似一个倒三角

大概是 。。这样

这里写图片描述

这样不好弄啊

那我们把他补一下 像这样

这里写图片描述
那么我们发现
只需要维护一个delta[ ]的前缀和 一个delta[x] * x 的前缀和就可以求出后边一部分式子了
即 红色部分 = delta[ ]前缀和(x+1) (矩形)- delta[x] x 前缀和(蓝色部分)
于是每次查询(l , r)就变成了

ans=( sum[r]+ deltasum[ r ] * (r+1) - deltaxsum[ r ])-( sum[ l-1 ] +deltasum[ l-1 ]* l -deltaxsum[ l-1 ] )

THE END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值