树状数组
来源:互联网 发布:java log4j stdout 编辑:程序博客网 时间:2024/05/16 04:18
树状数组(Binary Indexed Tree(BIT), Fenwick Tree)是一个查询和修改复杂度都为log(n)的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在log(n)的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。——百度百科
树状数组作用就是给定一个数组,在log(n)时间内求数组内某个区间的和,并且可以在log(n)时间内修改其中的某个元素。本文只研究修改单个值,区间求和的情况。
需要维护一个C数组,如下图所示:
{
return x&(x^(x–1));
}
{
return x&-x;
}
如果要修改一个值,那么需要修改所有管辖这个数的C[]数组的值。
比如要修改a[3],由于a[3]同时被c[3]、c[4]、c[8](假设只有8个数字)管辖,那么更新a[3]的时候,就需要同时修改c[3]、c[4]、c[8]。
修改代码:
void add(int k,int num) //将第k个元素加上num{ while(k<=n) { tree[k]+=num; k+=k&-k; } }
如果求1~n这个区间和的话,只需要将log(n)个C[]数组中的值想加即可。比如要求1~6这个区间的和,那就只需将C[6]和cC[4]相加即可。
求和代码:
int read(int k)//1~k的区间和 { int sum=0; while(k) { sum+=tree[k]; k-=k&-k; } return sum; }参考:http://www.cnblogs.com/ECJTUACM-873284962/p/6380245.html
整体代码:
#include <stdio.h>#include <string.h>#define N 1005int c[N], n;int lowbit(int x){return x&(-x);}void add(int k, int x){while(k <= n){c[k] += x;x += lowbit(x);}}int sum(int k)//求前k项和{ int s=0; while(k>0) { s+=c[k]; k-=lowbit(k); } return s;}int main(){int q, x, a, b;while(~scanf("%d%d", &n, &q)){memset(c, 0, sizeof(c));for(int i = 1; i <= n; i ++){scanf("%d", &x);add(i,x);}while(q--){scanf("%d%d", &a, &b);int ans = sum(b) - sum(a-1);printf("%d\n", ans);}}return 0;}
上面讲的都是一维树状数组,树状数组也可以扩展到二维,即求和时改为求某个子矩阵的和。
举个例子来看看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,C[1][6]=a15+a16,...
这是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,
C[2][5]=a15+a25,C[2][6]=a15+a16+a25+a26,...
这是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,C[4][3]=a13+a23+a33+a43,...
这是A[][]数组第一行+第二行+第三行+第四行后的树状数组
C[]数组在二维树状数组中需要同时管辖行和列,管辖的范围与一维相似,不论是行还是列都是管辖lowbit(i)的范围。
修改代码:
void modify(int x,int y,int data){ for(int i=x;i<MAXN;i+=lowbit(i)) for(int j=y;j<MAXN;j+=lowbit(j)) c[i][j]+=data;}
求和代码:
int get_sum(int x,int y){ int res=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) res+=c[i][j]; return res;}整体代码:
#include<iostream>#include <stdio.h>#include <string.h>#include<algorithm>using namespace std;const int MAXN = 1010;int c[MAXN][MAXN];int lowbit(int x){ return x & (-x);}void modify(int x,int y,int data){ for(int i=x;i<MAXN;i+=lowbit(i)) for(int j=y;j<MAXN;j+=lowbit(j)) c[i][j]+=data;}int get_sum(int x,int y){ int res=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) res+=c[i][j]; return res;}int main(){ int n,x,y,x1,y1, date; char str[5]; scanf("%d",&n); memset(c,0,sizeof(c)); while(n--) { scanf("%s",str); if(strcmp(str,"add")==0) { scanf("%d%d%d",&x,&y, &date); modify(x,y,date); } else { scanf("%d %d %d %d",&x,&x1,&y,&y1); int ans=get_sum(x1,y1)-get_sum(x-1,y1)-get_sum(x1,y-1)+get_sum(x-1,y-1); printf("%d\n",ans); } } return 0;}
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 树状数组
- 1.12.ARM的异常处理方式简单介绍
- 一:基本数据类型
- 如何用php截取文章的一段话以及第一个图片
- 微笑---面对所遭遇的一切
- iOS 百度地图~申请key及下载流程
- 树状数组
- MySQL基本函数介绍
- 从汇编视角看函数调用
- Partitioning by Palindromes UVA
- sublime如何使用sass
- getFlushMode is not valid without active transaction
- 最大子矩阵题目
- python中的类属性与实例属性
- 修改PDF文件中的图片用什么编辑器 怎么操作