树状数组和线段树的基础知识
- 线段树能处理的问题 包含 树状数组能处理的问题
- 而树状数组相对于线段树的优点:代码短;运行效率高
树状数组
- 能解决的核心问题:动态的快速求前缀和
- 可以在 O(logn) 的时间内,① 给某个位置上的数加上一个数(单点修改);② 求某一个前缀和(区间查询)
- 对比一下前缀和,单点修改是 O(n) (若修改某一个数,前缀和数组从这个数开始都会改变),区间查询是 O(1)
- 如果只有区间查询,前缀和更好(O(1) 优于 O(logn));如果两个操作都要有,树状数组更好(O(logn) 优于 O(n))
- 如何实现? (树状数组C是一维数组)
C[1]=A[1]C[2]=A[2]+C[1]=A[2]+A[1]C[3]=A[3]C[4]=A[4]+C[3]+C[2]=A[4]+…+A[1]C[5]=A[5]C[6]=A[6]+C[5]=A[6]+A[5]C[7]=A[7]C[8]=A[8]+C[7]+C[6]+C[4]=A[8]+…+A[1]
- 若有 C[x],x 的二进制表示的末尾有 k 个连续 0,即表示它在第 k 层,则 C[x] 表示A数组 (x−2k,x] 区间中所有数的和
- 因为 lowbit 操作: lowbit(x)=x&−x=2k,所以 C[x] 表示A数组 (x−lowbit(x),x] 区间中所有数的和(核心)
- 怎么计算前缀和?(区间查询)
S[x]=c[x]+c[x−lowbit(x)]+…
for(inti=x;i;i−=lowbit(i))res+=C[i]; - 怎么给某个位置上的数加上一个数?(单点修改)
要使A[x]+v: (x 的父节点是 x+lowbit(x))
for(inti=x;i<=N;i+=lowbit(i))C[i]+=v;
单点修改也用于树状数组 C[x] 的初始化(见以下例题) - AcWing 1264.动态求连续区间和 和 题解
- AcWing 1265.数星星 和 题解
线段树
- 形式:
区间[L,R],分割为[L,M]和[M+1,R],M=⌊L+R2⌋ - 单点修改操作:O(logn)
- 区间查询操作:O(logn)
- 核心函数
① pushup:用子节点信息更新当前节点信息
② build:在一段区间上初始化线段树
③ modify:单点修改
④ query:区间查询 - 线段树的存储:(与堆的存储类似,存储到一维数组中)
存储到一维数组的下标x的位置{父节点=⌊x2⌋x≫1左儿子=2xx≪1右儿子=2x+1x≪1∣1 - AcWing 1264.动态求连续区间和 和 题解