树状数组初步_ZERO
原博客:树状数组
1 一维树状数组
1 什么是树状数组
树状数组是一个查询和修改复杂度都为log(n)的数据结构,假设数组A[1..n],那么查询A[1]+…+A[n]的时,间是log级别的,而且是一个在线的数据结构。
2 树状数组作用
我们经常会遇到动态连续和查询问题,给定n个元素A[1~N],让我们求sum[L,R] = A[L]+…+A[R],或者更改A[i]的值。
假设数据很小的时候,那么我们利用暴力就可以搞定,这个时候更改A[i]的复杂度为O(1),但是求和的复杂度为O(n),如果有m次求和就是O(n*m),但是m很大的时候这个方法显然是不能够满足效率的要求。这个时候我们引入树状数组,树状数组的求和和更新都是O(logN),所以大大的降低了复杂度。
3 具体分析
1 建立树状数组就是先把A[] 和 C[]清空,然后假设有n个数那么就是做n次的update()操作就是建立树状数组,所以总的时间复杂度为O(nlogn)。
2 设原数组为A[1..N],树状数组为c[1..N],其中c[k] = A[k-(2^t)+1] + … + A[k]。比如c[6] = A[5] + A[6]。
假设 A为被计数数组,C为树状数组(计数)
0000 0001:C1 = A1
0000 0010:C2 = A1 + A2
0000 0011:C3 = A3
0000 0100:C4 = A1 + A2 + A3 + A4
0000 0101:C5 = A5
0000 0110:C6 = A5 + A6
0000 0111:C7 = A7
0000 1000:C8 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8
…
0001 0000:C16 = A1 + A2 + A3 + A4 + A5 + A6 + A7 + A8+ A9 + A10 + A11 + A12 + A13 + A14 + A15+ A16
3 也就是说,把k表示成二进制1***10000,那么c[k]就是A[1***00001] + A[1***00010] + … + A[1***10000] 这一段数的和。
4 设一个函数lowbit(k)为取得k的最低非零位,容易发现,根据上面的表示方法,从A[1]到A[k]的所有数的总和即为
sum[k] = c[k] + c[k-lowestbit(k)] + c[k-lowestbit(k)-lowestbit(k-lowestbit(k))] + … 于是可以在logk的时间内求出sum[k]。
5 当数组中某元素发生变化时,需要改动的c值是c[k],c[k+lowestbit(k)], c[k+lowestbit(k)+lowestbit(k+lowestbit(k))] … 这个复杂度是logN (N为最大范围)
6 如果题目要求sum[L , R] = sum[R]-sum[L-1]
sum[L-1] = A[1]+A[2]+…+A[L-1]
sum[R] = A[1]+A[2]+…+A[L]+…+A[R]
sum[R]-sum[L-1] = A[L]+A[L+2]+…+A[R]
7
树状数组的下标严格从1开始,所以如果出现0的情况要注意加1.(因为lowbit(0)是0所以如果出现为0的时候会进入无限循环中) , 树状数组中的每个元素至少含有它本身的一个值。
2 二维树状数组
1 二维树状数组说白了就是每一维都是树状数组
问题:一个由数字构成的大矩阵,能进行两种操作
1 对矩阵里的某个数加上一个整数(可正可负)
2 查询某个子矩阵里所有数字的和,要求对每次查询,输出结果。
2 一维树状数组很容易扩展到二维,在二维情况下:数组A[][]的树状数组定义为:
C[x][y] = ∑ a[i][j], 其中,x-lowbit(x) + 1 <= i <= x , y-lowbit(y) + 1 <= j <= y.
3 例:举个例子来看看C[][]的组成。
设原始二维数组为:
A[][]={{a11,a12,a13,a14,a15,a16,a17,a18,a19},
{a21,a22,a23,a24,a25,a26,a27,a28,a29},
{a31,a32,a33,a34,a35,a36,a37,a38,a39},
{a41,a42,a43,a44,a45,a46,a47,a48,a49}};
那么它对应的二维树状数组C[][]呢?
记:
B[1]={a11,a11+a12,a13,a11+a12+a13+a14,a15,a15+a16,…} 这是第一行的一维树状数组
B[2]={a21,a21+a22,a23,a21+a22+a23+a24,a25,a25+a26,…} 这是第二行的一维树状数组
B[3]={a31,a31+a32,a33,a31+a32+a33+a34,a35,a35+a36,…} 这是第三行的一维树状数组
B[4]={a41,a41+a42,a43,a41+a42+a43+a44,a45,a45+a46,…} 这是第四行的一维树状数组
那么:
C[1][1] = a11 , C[1][2] = a11+a12 , C[1][3] = a13 , C[1][4] = a11 + a12 + a13 + a14 , c[1][5]=a15.这是A[][]第一行的一维树状数组
C[2][1] = a11 + a21 , C[2][2] = a11 + a12 + a21 + a22 , C[2][3] = a13 + a23 , C[2][4] = a11 + a12 + a13 + a14 + a21 + a22 + a23 + a24 这是A[][]数组第一行与第二行相加后的树状数组
C[3][1] = a31 , C[3][2] = a31 + a32 , C[3][3] = a33 , C[3][4] = a31 + a32 + a33 + a34 , C[3][5] = a35 , C[3][6]=a35+a36,…这是A[][]第三行的一维树状数组
C[4][1] = a11 + a21 + a31 + a41 , C[4][2] = a11 + a12 + a21 + a22 + a31 + a32 + a41 + a42 ,这是A[][]数组第一行+第二行+第三行+第四行后的树状数组
3 树状数组的两类操作
1 单点更新,区间求和
1 一维树状数组,单点更新,区间求和
比如要更新点x ,x点的值加上val即调用add(x , val) , 求区间[1 , x]的和即为getSum(x)
- int lowbit(int x){
- return x&(-x);&n
bsp; - }
- int getSum(int x){
- int sum = 0;
- while(x){
- sum += treeNum[x];
- x -= lowbit(x);
- }
- return sum;
- }
- void add(int x , int val){
- while(x < MAXN){
- treeNum[x] += val;
- x += lowbit(x);
- }
- }
int lowbit(int x){ return x&(-x); } int getSum(int x){ int sum = 0; while(x){ sum += treeNum[x]; x -= lowbit(x); } return sum; } void add(int x , int val){ while(x < MAXN){ treeNum[x] += val; x += lowbit(x); } }
2 二维树状数组,单点更新,区间求和
比如要更新点(x , y) ,(x , y)点的值加上val即调用add(x , y , val) , 求矩形[1 , 1] - [x , y]的和即为getSum(x , y)
如上图求矩形的面积为getSum(x2 , y2)-getSum(x1-1,y2)-getSum(x2,y1-1)+getSum(x1-1 , y1-1)
- int lowbit(int x){
- return x&(-x);
- }
- int getSum(int x , int y){
- int sum = 0;
- for(int i = x ; i > 0 ; i -= lowbit(i))
- for(int j = y ; j > 0 ; j -= lowbit(j))
- sum += treeNum[i][j];
- return sum;
- }
- void add(int x , int y , int val){
- for(int i = x ; i < MAXN ; i += lowbit(i))
- for(int j = y ; j < MAXN ; j += lowbit(j))
- treeNum[i][j] += val;
- }
int lowbit(int x){ return x&(-x); } int getSum(int x , int y){ int sum = 0; for(int i = x ; i > 0 ; i -= lowbit(i)) for(int j = y ; j > 0 ; j -= lowbit(j)) sum += treeNum[i][j]; return sum; } void add(int x , int y , int val){ for(int i = x ; i < MAXN ; i += lowbit(i)) for(int j = y ; j < MAXN ; j += lowbit(j)) treeNum[i][j] += val; }
2 区间更新,单点求和
1 一维树状数组
更改区间[x , y],区间[x , y]里面的每个数全部加上val , 查询点k的值
区间[x , y]加上val相当于点x加上val , 点y+1减去val,那么求k点的值就等于[1,k]的和
- int lowbit(int x){
- return x&(-x);
- }
- int getSum(int x){
- int sum = 0;
- while(x){
- sum += treeNum[x];
- x -= lowbit(x);
- }
- return sum;
- }
- void add(int x , int val){
- while(x < MAXN){
- treeNum[x] += val;
- x += lowbit(x);
- }
- }
- void solve(){
- // 把区间[x , y]每一点加上val
- add(x , val);
- add(y+1 , -val);
- // 计算点k的值
- int num = getSum(k);
- }
int lowbit(int x){ return x&(-x); } int getSum(int x){ int sum = 0; while(x){ sum += treeNum[x]; x -= lowbit(x); } return sum; } void add(int x , int val){ while(x < MAXN){ treeNum[x] += val; x += lowbit(x); } } void solve(){ // 把区间[x , y]每一点加上val add(x , val); add(y+1 , -val); // 计算点k的值 int num = getSum(k); }
2 二维树状数组
更改矩形[x1 , y1] - [x2 , y2],[x1 , y1] - [x2 , y2]里面的每个数全部加上val , 查询点(x , y)的值
矩形[x1 , y1] - [x2 , y2]里面的每一个元素加上val相当于点(x1 , y1)加上val , 点(x1 , y2+1)减去val,点(x2+1 , y1)减去val , 点(x2+1 , y2+1)加上val。那么求某个点(x , y)的值即求[1 , 1] - [x , y]的和
- int lowbit(int x){
- return x&(-x);
- }
- int getSum(int x , int y){
- int sum = 0;
- for(int i = x ; i > 0 ; i -= lowbit(i))
- for(int j = y ; j > 0 ; j -= lowbit(j))
- sum += treeNum[i][j];
- return sum;
- }
- void add(int x , int y , int val){
- for(int i = x ; i < MAXN ; i += lowbit(i))
- for(int j = y ; j < MAXN ; j += lowbit(j))
- treeNum[i][j] += val;
- }
- void solve(){
- // 矩形[x1 , y1]-[x2 , y2]每个点加上val
- add(x1 , y1 , val);
- add(x2+1 , y1 , -val);
- add(x1 , y2+1 , -val);
- add(x2+1 , y2+1 , val);
- // 求点(x , y)的值
- int num = getSum(x , y);
- }
int lowbit(int x){ return x&(-x); } int getSum(int x , int y){ int sum = 0; for(int i = x ; i > 0 ; i -= lowbit(i)) for(int j = y ; j > 0 ; j -= lowbit(j)) sum += treeNum[i][j]; return sum; } void add(int x , int y , int val){ for(int i = x ; i < MAXN ; i += lowbit(i)) for(int j = y ; j < MAXN ; j += lowbit(j)) treeNum[i][j] += val; } void solve(){ // 矩形[x1 , y1]-[x2 , y2]每个点加上val add(x1 , y1 , val); add(x2+1 , y1 , -val); add(x1 , y2+1 , -val); add(x2+1 , y2+1 , val); // 求点(x , y)的值 int num = getSum(x , y); }
5 常用的技巧
假设初始化数组每个点的值为1,那么我们知道对于一维的树状数组来说,我们知道treeNum[i] = lowbit(i) . 对于二维树状数组来说treeNum[i][j] = lowbit(i)*lowbit(j)
- void init(){
- // 一维
- memset(treeNum , 0 , sizeof(treeNum));
- for(int i = 1 ; i < MAXN ; i++){
- num[i] =1;
- treeNum[i] = lowbit(i);
- }
- // 二维
- memset(treeNum , 0 , sizeof(treeNum));
- for(int i = 1 ; i < MAXN ; i++){
- for(int j = 1 ; j < MAXN ; j++){
- num[i][j] = 1;
- treeNum[i][j] = lowbit(i)*lowbit(j);
- }
- }
- }
void init(){ // 一维 memset(treeNum , 0 , sizeof(treeNum)); for(int i = 1 ; i < MAXN ; i++){ num[i] =1; treeNum[i] = lowbit(i); } // 二维 memset(treeNum , 0 , sizeof(treeNum)); for(int i = 1 ; i < MAXN ; i++){ for(int j = 1 ; j < MAXN ; j++){ num[i][j] = 1; treeNum[i][j] = lowbit(i)*lowbit(j); } } }
树状数组初步_ZERO的更多相关文章
- Matrix+POJ+二维树状数组初步
...
- Day2:T4求逆序对(树状数组+归并排序)
T4: 求逆序对 A[I]为前缀和 推导 (A[J]-A[I])/(J-I)>=M A[j]-A[I]>=M(J-I) A[J]-M*J>=A[I]-M*I 设B[]=A[]-M*( ...
- Stars(二维树状数组)
Stars Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others) Total Submiss ...
- HDU_2642_二维树状数组
Stars Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/65536 K (Java/Others)Total Submi ...
- [CSP-S模拟测试]:天才绅士少女助手克里斯蒂娜(数学+树状数组)
题目描述 红莉栖想要弄清楚楼下天王寺大叔的显像管电视对“电话微波炉(暂定)”的影响. 选取显像管的任意一个平面,一开始平面内有个$n$电子,初始速度分别为$v_i$,定义飘升系数为$$\sum \li ...
- [树状数组]数星星 Stars
数 星 星 S t a r s 数星星 Stars 数星星Stars 题目描述 天空中有一些星星,这些星星都在不同的位置,每个星星有个坐标.如果一个星星的左下方(包含正左和正下)有 k k k 颗星星 ...
- BZOJ 1103: [POI2007]大都市meg [DFS序 树状数组]
1103: [POI2007]大都市meg Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 2221 Solved: 1179[Submit][Sta ...
- bzoj1878--离线+树状数组
这题在线做很麻烦,所以我们选择离线. 首先预处理出数组next[i]表示i这个位置的颜色下一次出现的位置. 然后对与每种颜色第一次出现的位置x,将a[x]++. 将每个询问按左端点排序,再从左往右扫, ...
- codeforces 597C C. Subsequences(dp+树状数组)
题目链接: C. Subsequences time limit per test 1 second memory limit per test 256 megabytes input standar ...
随机推荐
- WireX:Android智能手机组成的DDoS僵尸网络
阿里聚安全小编曾多次报道了官方应用市场出现恶意软件的事件,让大家在下载APP的时候三思而后行. 最近多家安全公司组成的安全研究小组发现了一个新的.传播广泛的僵尸网络,它是由成千上万的Android智能 ...
- Druid使用记录
最近项目稳定下来,就像折腾一下看看系统的运行情况,但是我们搞java的毕竟不是专业运维,看看数据库的运行情况就ok了. 1 Druid介绍 官方地址 https://github.com/alibab ...
- JavaSE二次学习之标识符和编程命名相关的内容
前段时间阿里开源了<阿里巴巴 JAVA 开发手册>,里面详细叙述了有关编程命名.sql规约.工程规约等内容,作为一个初学者,只讨论一下-编程规约-的部分. 这几天又重新回去看了看JavaS ...
- XWPFRun属性详解
XWPFRun是XWPFDocument中的一段文本对象(就是一段文字) 创建文档对象 XWPFDocument docxDocument = new XWPFDocument(); 创建段落对象 X ...
- 怎样通过js 取消input域的hidden属性使其变的可见
document.getElementById(ID).setAttribute("hidden",false);厉害了 我的哥!
- 初学者入门web前端:C#基础知识:函数
入行前端对函数的掌握程度有可能直接影响以后工作的效率,使用函数可以高效的编写编码,节省时间,所以我整理了C#中最基础的函数知识点,虽然我在学习中 遇到很多问题,但是只要能够解决这些问题,都是好的. 一 ...
- MySQL(十一)之触发器
上一篇介绍的是比较简单的视图,其实用起来是相对比较简单的,以后有什么更多的关于视图的用法,到时候在自己补充.接下来让我们一起了解一下触发器的使用! 一.触发器概述 1.1.什么是触发器 触发器(Tri ...
- setAttribute设置无效
我发现ie浏览器中动态用setAttribute设置style属性值始终不能设置,经过一番查找发现了这篇文字 http://webcenter.hit.edu.cn/articles/2009/05- ...
- 在windows中,使用SSH登录VMware ubuntu linux虚拟机
测试环境 主机:window7 sp1 64位 专业版 虚拟机:VMware workstation 12 player 虚拟机操作系统: ubuntu 16.4 目标:在ubuntu下运行SSH服务 ...
- 全平台轻量级 Verilog 编译器 & 仿真环境
一直苦于 modelsim 没有Mac版本,且其体量过大,在学习verilog 时不方便使用. 终于找到一组轻量级且全平台 ( Linux+Windows+macOS ) 的编译仿真工具组. Icar ...