树状数组
&树状数组基础
树状数组是一个查询与修改操作时间复杂度都为log(n)的数据结构,主要用于数组的 单点修改 and 区间查询(求和)
树状数组与线段树
具体区别和联系(baidu):
1.两者在复杂度上同级, 但是树状数组的常数明显优于线段树, 其编程复杂度也远小于线段树.
2.树状数组的作用被线段树完全涵盖, 凡是可以使用树状数组解决的问题, 使用线段树一定可以解决, 但是线段树能够解决的问题树状数组未必能够解决.
3.树状数组的突出特点是其编程的极端简洁性, 使用lowbit技术可以在很短的几步操作中完成树状数组的核心操作,其代码效率远高于线段树。
引入
树状数组基本框架:
用数组进行填充:
实际结构:
红色为树状数组 黑色为原数组
树状数组的意义:
c[1]=a[1];
c[2]=a[1]+a[2];
c[3]=a[3];
c[4]=a[1]+a[2]+a[3]+a[4];
c[5]=a[5];
c[6]=a[5]+a[6];
c[7]=a[7];
c[8]=a[1]+a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8];
将原数组下标的二进制存入树状数组:
发现:c[i]数组记录的数据与二进制下“0”的数量有关
c[i]记录的数据包含的个数是i的二进制下从右向左第一个1的位置的十进制数的大小 通过对第一个1的位置进行简单处理,可以维护这样的树状数组
&进入正题
模块零 建树
树状数组的构建==对每个点进行修改(单点修改)
模块一 lowbit
代码:
int lowbit(int x){return x & (-x);}
lowbit()函数用来取一个二进制最低位的一与后边的0组成的数
例:5(101),lowbit(5)=1(1)
12(1100),lowbit(12)=4(100)
int lowbit(int t)
{
return t&(-t);
}
原理,二进制数的负数是正数取反加一
12(1100),-12(0100)
图中蓝色剪头表示当前数字+lowbit(i)=箭头指向的数 绿色箭头表示当前数字-lowbit(i)=箭头指向的数字
i为奇数时:按以上方法进行i&(-i)得到的数一定为1
i为偶数时:按以上方法进行i&(-i)得到的数会是最后一位1到后面的0
模块二 修改&查询
单点修改 & 区间查询
区间修改 & 单点查询
模板题:
单点修改 & 区间查询
void add(int x,int k){
while(x<=n){
a[x] += k;
x += lowbit(x);
}
}
区间修改 & 单点查询
int sum(int x){
int ans = 0;
while(x != 0){
ans += a[x];
x -= lowbit(x);
}
return ans;
}
模板完整代码:
单点修改 & 区间查询
//P3374 【模板】树状数组 1
/*
Worker:zcxxxxx
*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1e5 + 7;
const int INF = 0x7fffffff;
const int mod = 1e8 - 3;
inline ll read() {
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int lowbit(int x){
return x & -x;
}
void add(int x, int k){
while(x <= n){
a[x] += k;
x += lowbit(x);
}
}
int sum(int x){
int ans = 0;
while(x != 0){
ans += a[x];
x -= lowbit(x);
}
return ans;
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++){
int z = read();
add(i, z);
}
for(int i = 1; i <= m; i++){
int z=read(), x=read(), y=read();
if(z == 1) add(x, y);
if(z == 2) cout << sum(y) - sum(x - 1) << endl;
}
return 0;
}
区间修改 & 单点查询
/*
Worker:zcxxxxx
*/
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e6 + 7;
const int INF = 0x3f3f3f3f;
inline ll read() {
ll x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int lowbit(int x){
return x & -x;
}
void add(int x, int k){
while(x <= n){
a[x] += k;
x += lowbit(x);
}
}
int sum(int x){
int ans = 0;
while(x != 0){
ans += a[x];
x -= lowbit(x);
}
return ans;
}
int main(){
n = read(), m = read();
for(int i = 1; i <= n; i++){
int z = read();
add(i, z);
}
for(int i = 1; i <= m; i++){
int z = read(), x = read(), y = read();
if(z == 1) add(x, y);
if(z == 2) cout << sum(y) - sum(x - 1) << endl;
}
return 0;
}