树状数组详解(超详细)(完整代码在四 五最后)
一,树状数组的优点
前缀和的思想,可以通过O(n)的预处理,使得多次查询区间值都是o(1),但只能解决不修改,多次查询的问题。
差分思想,能通过差分数组,将区间修改变成O(1)的,最后通过一次O(n),可以恢复成原来的数组,但只能解决多次修改一次询问。
那有没有一种方法,一边修改一边询问,且时间复杂度是可以接受的呢?
当然有,那就是树状数组,代码量小,且可以实现上述操作
二,了解树状数组前置知识点
熟悉位运算,略微涉及补码和补码相关知识
1),补码是计算机中存储整数的方法
第一位是符号位,0表示正数,1表示负数
正数的原码反码补码,三码相同,下面讲的是负数的情况。
反码就是对原码进行取反操作(符号位不变,其他位取反)
补码就对原码先反码,然后再加一
例如:-8 原码 :10001000 反码 11110111 补码 11111000
那如何通过上述知识来获取一个数字最后一位1呢?
我们以40为例,还是假设八位
Lowbit(40)=40&-40=00101000 & 11011000=00001000=8
&表示按位与,即全是1才为1
三,树状数组如何查询区间和
给定一个原数组A,下标 1~n ,
新建一个数组c ,让其中每一个数字,掌管一个区间和。
注:以下内容只需先理解是怎么运行,不用明白原理,因为后文会解释
c[x] 就掌管长度为lowbit(x)的范围,即掌管范围是[x-lowbit(x)+1,x]
这样要询问1~x的前缀和,可以先用lowbit(x)得到前缀里面的一段子区间和,然后x-lowbit(x)
重复操作得到其他区间和,因为只用算log次lowbit,所以log次lowbit便可以得到结果,所以求[L,R],只要算两个前缀和,再相减即可。
下面为代码
int lowbit(int x){return x&(-x);
}int query(int x){ //求1~x的区间和 int ans=0;while(x){ans+=d[x];x-=lowbit(x);}return ans;
}
那么为什么可以这样呢?
首先先看一个树状数组的图
解释一下上图:A为原数组 C为树状数组(每一位存了他掌管的区间)即i-lowbit(i)+1到 i
C[1] lowbit(1)=1 范围 1-1
C[2] lowbit(2)=2 范围 1-2
C[3] lowbit(3)=1 范围 3-3
C[4] lowbit(4)=4 范围 1-4
C[5] lowbit(5)=1 范围 5-5
C[6] lowbit(6)=2 范围 5-6
C[7] lowbit(7)=1 范围 7-7
C[8] lowbit(8)=8 范围 1-8
而且有个重要性质当前节点位置i ,加上lowbit (i),即为它的父亲节点(可带入上图验证)
四,树状数组如何单点更新
若我们要让A i加上 4,那我们就让树状数组中全部包含A i的c i都加上 4
所以接下来就要求,找到包含Ai的所有区间 Ci
之前已经提过 lowbit 不光是掌控区间大小,还是当前点与父亲节点的距离
所以单点更新时,只要每次下标加lowbit ,且更新节点信息即可。
代码如下
void add(int x,int k){while(x<=n){d[x]+=k;x+=lowbit(x);}
}
完整代码如下
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int s[500050],d[500050];
int lowbit(int x){return x&(-x);
}void add(int x,int k){while(x<=n){d[x]+=k;x+=lowbit(x);}
}int query(int x){ //求1~x的区间和 int ans=0;while(x){ans+=d[x];x-=lowbit(x);}return ans;
}
signed main()
{ cin>>n>>m;for(int i=1;i<=n;i++){cin>>s[i];}for(int i=1;i<=n;i++){add(i,s[i]);}for(int i=1;i<=m;i++){int op,x,k;cin>>op;if(op==1){cin>>x>>k;add(x,k);}else{cin>>x>>k;int sum1=query(k);int sum2=query(x-1);cout<<sum1-sum2<<endl;}}return 0;
}
五,树状数组的区间更新(还是推荐用线段树来维护区间更新)
树状数组是可以支持区间更新的,但是线段树在思维上更有优势,并且可拓展性强
A 还是原数组,D 表示差分数组,D[i]=A[i]-A[i-1],且让D1=A1
这样我们用树状数组Ci来维护Di这个差分数组,而不是Ai原数组
求Ai变成了D1 到Di的前缀和,用树状数组实现
如过要让 L-R区间加上x ,就变成了单点更新,即让D[L] 加x D[R+1] 减去x
ok下面是区间更新,单点查询完整代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int s[500050],d[500050];
int lowbit(int x){return x&(-x);
}void add(int x,int k){while(x<=n){d[x]+=k;x+=lowbit(x);}
}int query(int x){ //求1~x的区间和 int ans=0;while(x){ans+=d[x];x-=lowbit(x);}return ans;
}
signed main(){ cin>>n>>m;for(int i=1;i<=n;i++){cin>>s[i];}for(int i=1;i<=n;i++){add(i,s[i]-s[i-1]); }for(int i=1;i<=m;i++){int op;cin>>op;if(op==1){int x,y,k;cin>>x>>y>>k;add(x,k);add(y+1,-k);}else{int x;cin>>x;cout<<query(x)<<endl;}}return 0;
}
树状数组详解(超详细)(完整代码在四 五最后)相关推荐
- 【数据结构】树状数组详解(Leetcode.315)
前言 最近做题时遇到一个关于树状数组的题力扣https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/但是CSDN上仅有 ...
- 树状数组详解(附图解,模板及经典例题分析)
导言 深藏于算法与数据结构中的思想非常的美妙,尤其是当我们一个一个攻克其中的难点,体会其中蕴含的"哲理"时, A 题的自信力也会有所增加,心情也会格外的舒爽.最近重新接触了树状数组 ...
- szu 寒训第二天 树状数组 二维树状数组详解,以及树状数组扩展应用【求逆序对,以及动态第k小数】
树状数组(Binary Index Tree) 树状数组可以解决可以转化为前缀和问题的问题 这是一类用以解决动态前缀和的问题 (有点像线段树简版) 1.对于 a1 + a2 + a3 + - + an ...
- AcWing 241 楼兰图腾(树状数组详解)
树状数组 问题引入 树状数组是一种实现起来比较简单的高级数据结构. 我们知道,对于一个数组a[i],其前缀和s[i]表示a数组里面前i个元素之和,而求区间l到r的元素之和可以用s[r] - s[l-1 ...
- Java 泛型详解(超详细的java泛型方法解析)
Java 泛型详解(超详细的java泛型方法解析) 1. 什么是泛型 泛型:是一种把明确类型的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型.也就是说在泛型使用过程中,操作的数据类型被指定为 ...
- 斜率优化详解(超详细, 有图有代码有注释)
文章目录 斜率优化引入 从例题开始 斜率优化Part 1: 推为斜率式 斜率优化Part 2: 合法点集的斜率单调性 Part 3: 找到最优决策点 Part 4: 斜率优化大流程 Part 5: 斜 ...
- 洛谷P3368 【模板】树状数组 2(Python和C++代码)
##就是常规写法 用树状数组维护一个差分数组的前缀和,因为可推得若b[i]=a[i]-a[i-1],则a[i]=b[1]+-+b[i] (b[1]=a[1]-a[0],a[0]=0) . 可发现a[i ...
- mysql 联表比对,MySQL联表查询详解/超详细mysql left join,right join,inner join用法分析比较...
超详细mysql left join,right join,inner join用法分析 下面是例子分析 表A记录如下: aID aNum 1 a20050111 2 a20050112 3 a200 ...
- log4j 配置详解(超详细)
一.Log4j简介 Log4j有三个主要的组件:Loggers(记录器),Appenders (输出源)和Layouts(布局).这里可简单理解为日志类别,日志要输出的地方和日志以何种形式输出.综合使 ...
最新文章
- raver php,为PhpStorm添加Laravel 代码智能提示功能
- 进程线程002 等待链表 调度链表
- linux 裸设备使用,linux裸设备使用
- C语言-freopen函数输入简单使用举例
- Python中Collections模块的Counter容器类使用教程
- 《云计算:原理与范式》一3.10 企业对企业集成服务
- matlab2c使用c++实现matlab函数系列教程-binostat函数
- 【问题记录】mysql设置任意ip访问
- 有用facs做计算机表情识别的嘛,人脸表情识别可以测谎吗
- Pensieve Multi_agent代码详解以及A3C强化学习代码详解
- Head First中文版教程
- 合并多个集合同类项{aaa,bbb,ccc},{bbb,ddd},{eee,fff},{ggg},{ddd,hhh}通过编程实现结果为:{aaa,bbb,ccc,ddd,hhh},{eee,fff},
- eacharts y轴留白策略
- 如何按要求批量修改Excel工作表名称
- java8中的lambda表达式实用详解
- 防拷贝加密U盘的功能有些
- 华为智慧屏“两年”,从技术创新到引领电视产业变革
- 蓝桥杯嵌入式定时器输出PWM(可调占空比)
- 运行时错误1004 应用程序定义或对象定义错误
- SQL Server 2019 开启数据库远程访问