树状数组入门

引入

什么是树状数组???

树状数组就是用数组来模拟树形结构 ,方便完成之后的操作

树状数组的作用是什么?有什么优势?

树状数组是为区间操作而生的 ,能够有效的解决单点更新 ,区间查询的操作可以将求和的时间复杂度从 O(n) 简化为 O(logn)

话不多说 ,先看例题

BIT-1

题目描述

共有 n 个数,进行 q 次操作 ,操作有两种:

1 i x : 将 a[i] 加上 x;

2 l r : 求 a[l] + ... + a[r]; 

第一行:输入两个数 n,q,表示给定数组的长度和操作数

第二行:输入n个数,表示长度为n的给定数组

接下来q行:每行输入 3 个数字,表示如题的某一种操作

对于每个 2 操作 ,输出对应结果

解题思路

如果采用最朴素的普通数组去做 ,修改为 O(1) ,查询为 O(n)

如果使用前缀和 ,修改为 O(n) ,查询为O(1)

既然这两种方法都会超时 ,我们就要用到树状数组

树状数组

· 构造

我们用 a 数组来存储原数组 ,用 c 数组来模拟树的结构 ,把 a 数组看成是这个树形结构的叶子 ,就得到了下面这张图:

每个节点中存储的元素就是它的子树的所有元素之和 ,

那数据的存储有什么规律呢?

根据这两张图片,我们可以发现,c[i] 的层数取决于 i 的二进制的末尾的 0 的个数 ,要找出共有多少个 0 ,就等于寻找最后一个 1 的位置 ,想要实现这个过程十分简单 ,只需要一个位运算

 

long long lowbit(long long x){
	return x & (-x);
}

· 单点修改

从图中 ,我们可以发现 i+lowbit(i) 所得的值是 i 的父节点 ,所以我们修改 a[i] ,c数组中 i 的所有祖先也都需要修改 ,顺着 i 一直往上找到父节点进行修改即可

void update(int x, int val){ 
	for(;x <= n;x += lowbit(x))
		c[x] += val;
}

· 查询

举例 :查询\sum_{i=1}^{7} a_{i} 的值 ,sum = c[7] + c[6] + c[4]

7 6 4 二进制分别为 111 110 100 ,每次减去 lowbit(i) 求和即可得出结果

long long query(long long x){
	long long sum = 0;
	for(;x;x -= lowbit(x))
		ans += c[x];
	return ans;
}

模板代码

#include<bits/stdc++.h>
using namespace std;
int n,q;
long long c[1000005];
int lowbit(int t){
    return t&(-t);
}
void add(int x,int k){
    for(int i=x;i<=n;i+=lowbit(i)){
        c[i]+=k;
    }
}
long long sum(int x){
    long long ans=0;
    for(int i=x;i;i-=lowbit(i)) ans+=c[i];
    return ans;
}
int main(){
    int x;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++){
    	scanf("%d",&x); add(i,x);
	}
    while(q--){
    	int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        if(x==1) add(y,z);
        else printf("%lld\n",sum(z)-sum(y-1));
    }
    return 0;
}

BIT-2

题目大意

共有 n 个数,进行 q 次操作 ,操作有两种:

1 l r k  将a[l]~a[r]每个数 + k

2 k 输出a[k]

对于每个操作 2  ,输出对应结果

解题思路

采用差分的思想 ,因为差分是前缀和的逆运算 ,所以我们用 c 来表示维护一个差分数组 ,每次update把 c[l] ~ c[k] 加 k ,每次query时计算c[1]~c[k]的前缀和即可

AC代码

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值