树状数组

一、前置知识

  1. 在树状数组中会用到一种位运算 : l o w b i t ( x ) = x & ( − x ) lowbit(x) = x \& (-x) lowbit(x)=x&(x)。下面来简单说明一下这种位运算的作用。

  2. 我们知道在计算机中,数值都是以补码形式存储的。所以对于一个正整数x来说,在计算机中 : -x = ~x + 1
    我们假设x的二进制表示末位有k个0,此时x和-x可以看作:
    ( x ) D = ( x x x 1 00...0 ⏞ k个0 ) B (x)_D=(xxx1\overbrace{00...0}^\text{k个0})_B (x)D=(xxx100...0 k0)B
    ( − x ) D = ( y y y 1 00...0 ⏞ k个0 ) B (-x)_D=(yyy1\overbrace{00...0}^\text{k个0})_B (x)D=(yyy100...0 k0)B
    其中 x x x表示未知数, y y y表示 x x x取反之后的数。
    此时我们可以看出
    x & ( − x ) = 1 00...0 ⏞ k个0 x \& (-x)=1\overbrace{00...0}^\text{k个0} x&(x)=100...0 k0

  3. 从以上分析可以看出:
    l o w b i t ( x ) = x & ( − x ) = 2 k lowbit(x) = x\&(-x)=2^{k} lowbit(x)=x&(x)=2k

二、树状数组简介

1. 作用

树状数组主要用来解决前缀和的问题。

2. 操作

树状数组有两个核心操作:

  1. 单点修改

  2. 区间查询

3. 存储方式

顾名思义,用数组去存树状数组。如果用a[ ]表示原数组,c[ ]表示树状数组,则c[i]表示的是这样一段和:
c [ i ] = a [ i − l o w b i t ( i ) + 1 ] + a [ i − l o w b i t ( i ) + 2 ] + … + a [ i ] = ∑ j = i − l o w b i t ( i ) + 1 i a [ j ] c[i]=a[i-lowbit(i) + 1]+a[i-lowbit(i)+2]+…+a[i]=\sum_{j = i - lowbit(i) + 1}^ia[j] c[i]=a[ilowbit(i)+1]+a[ilowbit(i)+2]++a[i]=j=ilowbit(i)+1ia[j]

ps : 数组下标要从1开始(在代码部分会有说明)
4. 树状数组图示

在这里插入图片描述

4.优点
  1. 代码简单,容易调试
  2. 修改操作和查询操作的时间复杂度都是O(logn)

三、代码部分

  1. 单点修改
/* 在x处加上v */
void add(int x, int v) 
	for(int i = x; i <= n; i += lowbit(i)) // x改变,所有包含x的c[]全部都要加上v
		c[i] += v;
}
  1. 区间查询
/* 查询[1, x]的前缀和 */
int query(int x) {
	int res = 0;
	for(int i = x; i; i -= lowbit(i)) 
		res += c[i];
	return res;
}

// 这里说明一下为什么树状数组下标从1开始:
// 假如从0开始, 当i = 0的时候, lowbit(0) = 0, 会陷入死循环

四、应用及题目

  1. 树状数组模板题:AcWing 1264 : 动态求连续区间和
  2. 动态查询乱序数组中的中间值:PAT 1057 : Stack
  3. (这个不知道叫什么应用, 直接贴链接^ ^):AcWing 1215 : 小朋友排队
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值