树状数组

&树状数组基础

树状数组是一个查询与修改操作时间复杂度都为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

模块二 修改&查询

单点修改 & 区间查询

区间修改 & 单点查询
模板题:

P3374 【模板】树状数组 1

P3368 【模板】树状数组 2

单点修改 & 区间查询

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;
}
posted @ 2022-01-01 11:29  zcxxxxx  阅读(40)  评论(0编辑  收藏  举报