首页 > 其他分享 >树状数组

树状数组

时间:2023-02-26 13:44:07浏览次数:40  
标签:26 树状 int lowbit 补码 数组

树状数组

总结一下树状数组。

作用

树状数组通常用于解决区间问题,总的来说它的用途没线段树广,但是常数比线段树小而且写法较为简单,所以还有一定用途。

原理

树状数组的一个核心是lowbit计算,其具体就是

int lowbit(int x)

首先有一点,计算机内储存整型类型变量试用补码储存的。

正数的补码是原码,负数的补码是将其原码除符号位外的所有位取反后加1。

那lowbit计算的结果是什么呢?

这里拿数字26举例。

26 补码:00011010 -26 原码:10011010 反码:11100101 补码:11100110

lowbit(26)=00000010

可以看出0010是26二进制下最右端1的位置。

通过感性理解,我们就可以发现lowbit的功能就是找出一个数二进制下最右端1的位置。

通过lowbit我们可以构造出一个怎样的数据结构呢?

单点修改区间查询

单点修改区间查询是树状数组最基本的功能。

luogu P3374 【模板】树状数组1

树状数组的功能是以lgn的复杂度完成单点修改和查询1~n的和的操作。

首先我们要知道树状数组f[n]的含义是什么。

\[f[n]=\sum_{i=n-lowbit(n)+1}^n a[i] \]

然后我们只需要对每个操作进行模拟就行了

单点修改

void add(int i,int x)

区间查询

int qry(int x){
​ int res=0;
​ for(int i=x;i;i-=lowbit(i))res+=f[i];
​ return res;
}

ans=qry(r,l-1);

区间修改单点查询

这里主要运用了差分的思想。

luoguP3368 【模板】树状数组 2

#include<bits/stdc++.h>
using namespace std;
const int N=500005;
int lowbit(int x){return x&-x;}
int n,m;
int f[N];
void add(int i,int x){for(;i<=n;i+=lowbit(i))f[i]+=x;}
int qry(int x){
	int res=0;
	for(int i=x;i;i-=lowbit(i))res+=f[i];
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,x;i<=n;++i){scanf("%d",&x);add(i,x);add(i+1,-x);}
	while(m--){
		int o,x,y,k;
		scanf("%d",&o);
		if(o==1){scanf("%d%d%d",&x,&y,&k);add(y+1,-k);add(x,k);}
		else {scanf("%d",&y);printf("%d\n",qry(y));}
	}
	return 0;
}
区间修改区间查询

通过一些构造树状数组也是可以完成区间修改区间查询的,但是最好平时还是用线段树会好一些,因为会比较直观。

这里放上代码。

luogu P3372 【模板】线段树 1

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int read(){
	int 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;}
ll c1[N],c2[N];
int n,m;
ll add(int x,ll y){for(int i=x;i<=n;i+=lowbit(i))c1[i]+=y,c2[i]+=y*x;}
ll get(int x){ll res=0;for(int i=x;i;i-=lowbit(i))res+=c1[i]*(x+1)-c2[i];return res;}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i){ll x=read();add(i,x);add(i+1,-x);}
	while(m--){
		int o=read();
		if(o==1){int l=read(),r=read();ll z=read();add(l,z);add(r+1,-z);}
		else {int l=read(),r=read();printf("%lld\n",get(r)-get(l-1));}
	}
	return 0;
}

标签:26,树状,int,lowbit,补码,数组
From: https://www.cnblogs.com/ADMAN/p/17156557.html

相关文章

  • 在排序数组中查找元素的第一个和最后一个位置---二分查找
    在排序数组中查找元素的第一个和最后一个位置给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果......
  • 搜索旋转排序数组---二分查找
    搜索旋转排序数组整数数组nums按升序排列,数组中的值互不相同。在传递给函数之前,nums在预先未知的某个下标k(0<=k<nums.length)上进行了旋转,使数组变为[nums[k]......
  • 数据结构:树状数组
    声明:全部代码未经编译,不保证正确性,仅限逻辑学习,请勿直接抄袭什么是树状数组树状数组,本质是运用二进制运算规则维护区间。它的效率高于线段树,空间也少于线段树。但是所能......
  • 动态数组代码实现
    一、动态数组一般在静态数组定义后,系统就会为其分配对应长度的连续的专有内存空间,可是,我们都知道,不同的运行样例,所需要的数组长度是不一样的,为了所有样例都可以执行,一般......
  • 二维数组的声明及其作为函数参数的方式
    ◆概要本文以3行2列的二维数组为例,介绍了如何声明自动存储、静态存储和动态存储的二维数组,及其如何将它们作为函数参数进行传递的方式。◆实现处理自动存储或静态......
  • 数组和指针
    一维数组和指针先回忆一下,数组是由一系列类型相同的元素组成。如:charch[4];/*4个字符的数组*/intin[4];/*4个整数的数组*/floatfl[4];/*4个浮点数的数......
  • 后缀数组做题笔记
    后缀数组笔记这里挂一个学弟学习笔记的链接:Link,大部分内容都学习自这里。按道理这篇文章会持续更新1.Preface​ 首先有几个概念需要明确。本文中所有字符串下标从\(1......
  • 二维字符数组
    当有多个字符串时,可以使用二维字符数组例如:chararr[3][128]={"hello","world","hehehe"};二维字符数组的遍历 ......
  • 数组
    1.返回一个数组中最大子数组的和要求:输入一个整形数组,数组里有正也有负数      数组中连续的一个或多个数组组成一个子数组,每个子数组都有一个和   ......
  • C++-4 数组
                   ......