浅谈树状数组

一般的树状数组是从1为开始的,因为每次操作是对最低位的1操作,也就是+-lowbit(i) (lowbit(i) = i & -i)。

也可以从0开始,这样每次操作就不是对最低位的1操作了,而是对最低位的0操作,也就是(lowbit(i) = ~i & i + 1)。


所以从0开始的树状数组可以这样写:

void update(int x, int val)  
{  
	for(int i = x; i <= lim; i += ~i & i + 1) tree[i] += val;
}  
void query(int x) {//查询0~x的个数
	int sum = 0;
	for(int i = x; ~i; i -= ~i & i + 1) sum += tree[i];
	return sum;
}

让我们来简单做个证明它的正确性。

对query(x)操作,假设x=(1000100100111)2.

第一步sum += tree[1000100100111], 得到的是1000100100000~1000100100111的所有个数

第二步sum += tree[1000100011111], 得到的是1000100000000~1000100011111的所有个数

第三步sum += tree[1000011111111], 得到的是1000000000000~ 1000011111111的所有个数

第四步sum += tree[0111111111111], 得到的是 0000000000000~ 0111111111111的所有个数


因为树状数组是位运算的产物,可以结合二分的位运算实现产生更华丽的应用。

比如这样的二分:

        for (int step = 20; step >= 0; -- step) {
            lb += 1 << step;
            if (lb > n || std::max(c,C[lb]) >= std::min(d,D[lb])) lb -= 1 << step;
            else c = std::max(c,C[lb]),d = std::min(d,D[lb]);
        }

这在二分下界lb,每次需要得到前缀1 ~ lb的最大值,每次不需要在树状数组上询问一下query(lb),而是在二分的过程中就可以一直更新1~lb的最大值,因为这是位运算实现的二分,每次lb += 1 << step以后,lowbit(lb)等于1 << step,所有只需要在二分过程中更新最大值就行了,这样复杂度就从logn * logn 降低到logn了。

但是有人会注意到,这个只有lowbit(lb) = lb & -lb这种树状数组才满足等于1<<step,没错。但从0开始的树状数组也可以二分,需要加一点技巧,lb初始值设为-1就可以了,这样每次 1 << step 就等于 lowbit(lb)了。

比如:-1 + 1000000 = 0111111 , lowbit(0111111) = 1000000



这个应用可见:http://codeforces.com/contest/689/problem/D

这是lowbit(x) = x & -x 的二分代码:http://codeforces.com/contest/689/submission/18978399

这是lowbit(x) = ~x & x + 1的 二分代码:http://codeforces.com/contest/689/submission/18933132



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值