ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

树状数组学习笔记

2022-01-30 20:00:37  阅读:113  来源: 互联网

标签:树状 int lowbit update 笔记 数组 inline op


树状数组

树状数组是一种高效的对列表更新和求前缀和的结构
对于已经学过的前缀和(O(1)修改 O(n)查询),考虑维护一部分的区间和,修改时需要修改若干位置,查询时也需要查询若干位置,以此把修改和查询的代价平衡。

灵感

对于如何维护一部分区间,考虑每个整数为若干个 2 的幂的和,将每个前缀拆分成若干不相交的区间,按二进制中 1 的个数来拆分。

定义

对于一个合法的 BIT 区间,一定满足 [i - 2 ^ k, i],其中 k 是 i 的二进制中末尾 0 的个数,对于 BIT 数组的表示, 我们选取右端点,记原数组为 A,BIT数组为 B,有

对此:

一个前缀最多包含log(n)个区间——一次查询访问log(n)个位置。

一个位置最多被log(n)个区间包含——一次修改影响log(n)个位置。

编程实现

lowbit 返回二进制下最后一个 1 所代表的数值

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

对于树状数组需要记住

-x == ~x + 1;
lowbit(x) = x & (-x);

单点加

inline void update(int x, int v){
  for(; x <= n; x += x & -x){
    tre[x] += v;
  }
}

求前缀和

inline int query(int x){
  int ret = 0;
  for(; x; x -= x & -x){
    ret += tre[x];
  }
  return ret;
}

建树

不需要额外空间,每一个节点都是所有与自己直接相连的儿子求和得到的,考虑倒着贡献每次确定完儿子的值,更新父亲

inline void init(){
  for(int i = 1; i <= n; i ++){
    tre[i] += a[i];
    int j = i + lowbit(i);
    if(j <= n){
        tre[j] += tre[i];
    }
  }
}

查询

返回值为前缀和

inline int query(int x){
	int res = 0;
	for(; x; x -= lowbit(x))
		res += tre[x];
	return res;
}

模板

P3374 【模板】树状数组 1

#include<bits/stdc++.h>
using namespace std;

/*int lowbit_brute(int n){
	for(int i = 0; i <= 31; i ++){
		if(n & (1 << i)){
			return 1 << i;
		}
	}
}

x & ( ~x + 1)
return n & (~n + 1);
return 1 << __builtin_ctz(n);

__builtin_ctz(n) //返回 n 的二进制末尾 0 的个数
__builtin_clz(n) //返回 n 的二进制前导 0 的个数*/

const int N = 1e6 + 5;
int tre[N], n; 
 
int lowbit(int n){
	return n & -n;
}

inline void update(int x, int v){ //第 x 位加 v  
	for(; x <= n; x += lowbit(x)) 
		tre[x] += v;
}

inline int query(int x){
	int res = 0;
	for(; x; x -= lowbit(x))
		res += tre[x];
	return res;
}
inline int ask(int l, int r){
	return query(r) - query(l - 1);
}

int main(){
	int m;
	cin >> n >> m;  
	for(int i = 1; i <= n; i ++){
		int t; cin >> t;
		update(i, t);
	}
	
	while(m --){
		int op, x, k;
		cin >> op >> x >> k;
		if(op == 1){
			update(x, k);
		}
		if(op == 2){
			cout << ask(x, k) << endl;
		}
	}
}

P3368 【模板】树状数组 2
注意对原数组的差分数组建树,query返回值为当前元素

#include<bits/stdc++.h>
using namespace std;

const int N = 600010;
int n, m;
int a[N], tre[N], s[N];

inline int lowbit(int n){
	return n & -n;
}

inline void update(int x, int v){
	for(; x <= n; x += lowbit(x))
		tre[x] += v;
}

inline void init(){
	for(int i = 1; i <= n; i ++){
		tre[i] += s[i];
		int j = i + lowbit(i);
		if(j <= n){
			tre[j] += tre[i];
		}
	}
}

long long query(int x) {
    long long ans = 0;
    while (x){
        ans += tre[x];
        x -= lowbit(x);
    }
    return ans;
}

int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
	}
	s[1] = a[1];
	for(int i = 2; i <= n; i ++){
		s[i] = a[i] - a[i - 1];
	}
	init();
	while(m --){
		int op, x, y, k;
		cin >> op;
		if(op == 1){
			cin >> x >> y >> k;
			update(x, k);
			update(y + 1, -k);
		}else
		if(op == 2){
			cin >> x;
			cout << query(x)<< endl;
		}
	}
}

标签:树状,int,lowbit,update,笔记,数组,inline,op
来源: https://www.cnblogs.com/William-Sg/p/15857483.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有