什么是树状数组?树状数组简单的来说就是将一个数组模拟树形结构。

树状数组有什么用?树状数组可以将求和的操作从O(n)操作简化为O(logn)。

如图所示,横线下方为a数组表示为初试数据;上方为数组c,利用树形结构存储a数组内的数据。我们列举出来的这些:

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数组都为对应的a数组的值。

②所有下标为2的k次幂的c数组都为对应a数组2的k次幂的前缀和。

③其它下标为偶数的c数组都是由该偶数前所有的值的和,减去该下标对应的最大的2的k次幂的值的和。

即c[i] =a[i - +1] + a[i -+2] + ... + a[i];   //k为i的二进制中从最低位到高位连续零的长度(该偶数的2进制数的最低位1的位置再-1。

当我们计算7的前缀和,即我们需要计算sum=c[7]+c[6]+c[4];

即我们可以拓展为SUM = C[i] + C[i-] + C[(i - ) - ] + .....;//k1k为i的二进制中从最低位到高位连续零的长度,k2为i-后从最低位到高位连续零的长度。

我们引入lowbit(x)函数,此函数操作为x&(-x)。这里的-x在计算机中是以补码的形式计算的。此函数的作用为:当x为0时,返回为0;当x为奇数时,返回1;当x为偶数时,且为2的m次方时,返回x;当x为偶数,却不为2的m次方的形式时,返回,其中k为二进制中从最低位到高位连续零的长度(该偶数的2进制数的最低位1的位置再-1)。

注意事项:树状数组能够有效的解决单点更新,区间查询的过程。

题目引入:树状数组

代码:

#include<iostream>
#include <stdio.h>
#include<string.h>
using namespace std;
int a[1000001];
int c[1000001];
int lowbit(int i){return i&(-i);
}
void update(int i,int x,int n){while(i<=n){c[i]+=x;i+=lowbit(i);}
}
int downdate(int i){int sum=0;while(i>0){sum+=c[i];i-=lowbit(i);}return sum;
}
int main(){int n,m;cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];update(i,a[i],n);}int x,y,z;for(int i=1;i<=m;i++){cin>>x>>y>>z;if(x==1)update(y,z,n);else cout<<downdate(z)-downdate(y-1)<<endl;}return 0;
}

区间更新,单点查询

我们使用差分数组来表示,即D[i]=a[i]-a[i-1](i>=1);//建议看这里,有比较详细的介绍和说明

区间更新:

例如我们需要更新区间[2,6],将[2,6]区间内的每一个都加上x,x可正可负;

则:

D[2]=(a[2]+x)-(a[1])  =a[2]-a[1]+x=D[2]+x

D[3]=(a[3]+x)-(a[2]+x)=a[3]-a[2]  =D[3]

D[4]=(a[4]+x)-(a[3]+x)=a[4]-a[3]  =D[4]

D[5]=(a[5]+x)-(a[4]+x)=a[5]-a[4]  =D[5]

D[6]=(a[6]+x)-(a[5]+x)=a[6]-a[5]  =D[6]

D[7]=(a[7])  -(a[6]+x)=a[7]-a[6]-x=D[7]-x

即若更新[2,6],将区间[2,6]区间内的每一个都加上x,则就相当于D[2]+x,D[7]-x。

我们推广到一般情况,如果更新区间[a,b],且将[a,b]区间内加上x,则相当于D[a]+x,D[b+1]-x。

单点查询:

若想查询a[n],则根据累加法:

a[n]=D[n]+a[n-1]

a[n-1]=D[n-1]+a[n-2]

...

a[3]=D[3]+a[2]

a[2]=D[2]+a[1]

a[1]=D[1]+a[0]

a[0]=0

这种情况我们则需要用树状数组了。

即用D数组来建立一个树状数组。

区间更新,区间查询

对于求[1,n]区间内的总和sum,我们有如下处理:

已知

=n*(D[1]+D[2]+...+D[n])-(0*D[1]+2*D[2]+...+(n-1)*D[n])

=

这样我们得到了两个,即可用于树状数组。

我们令c数组为(i-1)*D[i]。

所以,我们设置两个树状数组sum1[i]<-D[i]和sum2[i]<-c[i]。

区间更新:

若更新区间[a,b],并且在区间同时+x,对于sum1,我们可以在a处向上更新x,在b+1处更新-x;对于sum2,我们可以在a处向上更新(a-1)*x在b+1处向上更新b*-x;

区间查询:

若查询区间[l,r],我们可以找到区间[0,l-1]的值,向下查找sum1-sum2,找到[0,r],向下查找sum1-sum2;

代码:

#include<bits/stdc++.h>
int n,m;
int a[50005] = {0};
int sum1[50005];    //(D[1] + D[2] + ... + D[n])
int sum2[50005];    //(1*D[1] + 2*D[2] + ... + n*D[n])
int lowbit(int x){return x&(-x);
}
void updata(int i,int k){int x = i;    //因为x不变,所以得先保存i值while(i <= n){sum1[i] += k;//更新sum1sum2[i] += k * (x-1);//更新sum2i += lowbit(i);}
}
int getsum(int i){        //求前缀和int res = 0, x = i;while(i > 0){res += x * sum1[i] - sum2[i];//这里因为sum1没有乘以n,所以在出结果时成i -= lowbit(i);}return res;
}
int main(){cin>>n;for(int i = 1; i <= n; i++){cin>>a[i];updata(i,a[i] - a[i-1]);   //输入初值的时候,也相当于更新了值}//[x,y]区间内加上kupdata(x,k);    //A[x] - A[x-1]增加kupdata(y+1,-k);  //A[y+1] - A[y]减少k//求[x,y]区间和int sum = getsum(y) - getsum(x-1);return 0;
}

当然,对于区间查询,区间更新的问题,我么还可以使用线段树来处理。

树状数组求逆序对数

我们求逆序对数除了归并算法求,还可以树状数组求逆序对数。

步骤:

  • 离散化:这里所谓离散化就是将一个序列的相对大小表示出来。目的是为了方便数据存储。对于重复的数据,我们就依据数据的先后顺序来处理

例如序列:6 25 9 63 2

序列的离散化结果为2 4 3 5 1

例如序列5 60 5 3 2 3

序列的离散化结果为4 6 5 2 1 3

  • 树状数组求逆序对数:

我们怎样求逆序对数呢?

我们可以依次从后向前遍历,以遍历的时间为序列的顺序,向前查找并且更新。

例如此序列逆序为1 5 3 4 2,则第一次寻找1前面的数据有多少比1小(向下寻找),然后把1向上更新+1;第二次寻找5前面的数据,发现c[4]=1,即5前面有1个比5小的数,然后将5向上更新+1。

题目引入:树状数组求逆序对

代码:

#include<bits/stdc++.h>
using namespace std;
struct node{long long sum;long long j;long long k;
};
long long sum=0;
node a[50000005];
long long c[50000005];
long long lowbit(long long x){return x&(-x);
}
bool cmp(const node xx,const node yy){if(xx.sum==yy.sum&&xx.j<yy.j)return true;if(xx.sum<yy.sum)return true;else return false;
}
bool cmp1(const node xx,const node yy){if(xx.j<yy.j)return true;else return false;
}
int main(){long long n;cin>>n;for(long long i=1;i<=n;i++){cin>>a[i].sum;a[i].j=i;}sort(a+1,a+1+n,cmp);for(long long i=1,x=1;i<=n;i++){a[i].k=x;x++;}sort(a+1,a+1+n,cmp1);for(long long i=n;i>=1;i--){long long x=a[i].k;long long y=x;while(y>0){sum+=c[y];y-=lowbit(y); }while(x<=n){c[x]++;x+=lowbit(x);}}cout<<sum;return 0;
} 

树状数组(树状数组的基本用法与操作)相关推荐

  1. 吊打线段树的超级树状数组

    你是否讨厌线段树那冗长的代码?你是否还在因为线段树的难调试而满头♂dark汗?那么,请不要错过!超级树状数组特价!只要998,只要998! ##¥--#--¥%--&%¥--ER#%$#$#^ ...

  2. BZOJ1103 大都市 DFS序 树状数组维护差分数组

    BZOJ1103 大都市 问题描述 在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Mary也开始骑着摩托车传递邮件了. 不过,她经常回忆起以前在乡间漫步的情景.昔日,乡下有依次 ...

  3. Codeforces Round #439 (Div. 2) E. The Untended Antiquity 二维线段树||二维树状数组

    http://codeforces.com/contest/869/problem/E 题意:n*m的矩阵,q次操作,三种类型 类型1:给指定矩阵加上围栏 类型2:给指定矩阵去掉围栏 类型3:查询两点 ...

  4. 树形json扁平化,一维数组树状化,对象深拷贝,元素后插入新元素,格式或动态路由等常用js合集

    索引 一.在元素后面插入一个新的元素. 二.对象或者数组的深拷贝. 三.从服务器端获取到动态路由表的格式化. 四.json树形数据扁平化处理(变成一维数组) 五.一维数组转化为树状结构对象. 六.防抖 ...

  5. Acwing 4339 敌兵布阵 暴力 + 分块 + 线段树 + Zkw线段树 + 树状数组

    来一篇超全题解 数据结构大杂烩 原题连接 题目描述 敌人有 NNN 个工兵营地,编号 1∼N1∼N1∼N. 初始时,第 iii 个营地有 aia_iai​ 个人. 接下来有若干个命令,命令有 444 ...

  6. 树型结构——树状数组

    目录 1.树状数组的引入 2.基本操作:主要包括 插入操作,查询操作. 3.具体实现: 例题:1.楼兰图腾 241. 楼兰图腾 2.一个简单的整数问题 3.一个简单的整数问题2 1.树状数组的引入 树 ...

  7. (js)扁平数组树状化 树状数组扁平化

     扁平数组树状化(利用递归,两个函数完成树状转化 ) 数组格式: let list = [{ id: 1, title: '标题1', p_id: 0 },{ id: 2, title: '标题2', ...

  8. 数组树/fenwicktree/Binary Indexed Tree

    在解类似 leetcode 307题区域和检索 - 数组可修改的题时,我们可以使用一种比较小众的数据结构,数组树. 数组树的结构依托于数组,它的结构看起来类似下面这种: 1,2,3....代表他们在数 ...

  9. 庖丁解牛获取连接状态数的awk数组命令

    全部系列分为五篇文章,本博文为第二篇: 三.庖丁解牛获取连接状态数的命令 3.1 获取连接状态数的awk命令 netstat -n |awk '/^tcp/ {++S[$NF]} END {for(a ...

  10. 2022年3月24日蜻蜓q旗舰版v1.2.2更新-详细的用户,内容,整体圆饼+树状+横条状统计展示功能

    2022年3月24日蜻蜓q旗舰版v1.2.2更新-详细的用户,内容,整体圆饼+树状+横条状统计展示功能 更新日志 ·增加用户统计,用户画像,区分男性女性,月度统计,年度统计,注册用户数统计. ·增加评 ...

最新文章

  1. Leetcode 4.28 Tree Easy
  2. 跨源资源共享(CORS)漏洞修复
  3. 可变化的鸿蒙武器,DNF2018史诗改版大全 武器套装改版属性介绍
  4. Fetion2008 分析 Part1:准备工作
  5. STC芯片在Keil中的添加与使用
  6. xshell上传文件插件lrzsz
  7. 线程的虚假唤醒(Spurious Wakeups)以及解决方案
  8. Springboot系列之RestApi中获取枚举类属性的几种方式
  9. 部署nodejs报No package nodejs available
  10. Discuz最全的常见问题及故障整理
  11. SOCK_NONBLOCK,accept4 阻塞与非阻塞SOCKET
  12. 模电学习心得(转载)_史蒂文森sun_新浪博客
  13. C语言程序设计——猜数字游戏
  14. 3d建模网上学习靠谱吗?学3d建模哪个学校好?
  15. 【asm基础】nasm和masm的一些区别
  16. 数据库的基本操作(一)
  17. mac 部署nexus私服库
  18. seajs 的api接口
  19. Luogu P5037 抓捕
  20. 电子秤c语言编程,基于AT89C51的数字电子秤的设计最终版(样例3)

热门文章

  1. Android客户端与服务器用Socket进行通信
  2. SAP 总帐科目的批量传输
  3. win10 命令处理器已停止工作
  4. 绩效管理的本质是激发员工,而不是扣工资!
  5. Lasso introduction
  6. 王者荣耀:达摩长头发了?星座皮肤再添大将,后羿CP皮肤来袭!
  7. 重学 Java 设计模式:实战观察者模式「模拟类似小客车指标摇号过程,监听消息通知用户中签场景」
  8. 配置resolve映射路径
  9. 网易/美团/PingCAP/贝壳/爱奇艺大咖分享:云原生如何加速行业创新发展(附PPT下载)
  10. MySQL大量数据迁移解决办法