树状数组
2009年8月26日 04:04
新开的blog,也不知道写什么是好。
正好这几天在看树状数组方面的东西,这里就写些这方面的东西吧!
树状数组是一个查询和修改复杂度都为log(n)的数据结构。一般来说,树状数组用来解决区间求和问题。具体的算法过程这里就不介绍了。下面是一个动态查询区间和的程序:
#include <cstdio> #define MAX 1000 int c[MAX+1]; int n; int lowbit(int x) { return x&(-x); } void change(int x,int k) { while(x<=n){ c[x]+=k; x=x+lowbit(x); } } int search(int x) { int s=0; while(x>0){ s+=c[x]; x=x-lowbit(x); } return s; } int main() { scanf("%d",&n); char s[3]; int x,y,k; while(scanf("%s",&s)!=EOF){ if(s[0]=='C' || s[0]=='c'){ scanf("%d%d",&x,&k); change(x,k); } else if(s[0]=='Q' || s[0]=='q'){ scanf("%d%d",&x,&y); printf("%d\n",search(y)-search(x-1)); } } return 0; }
Sample Input
10
Q 5 6
C 4 5
Q 1 5
C 7 9
Q 1 10
C 5 70
Q 3 10
Q 5 6
Sample Input
0
5
14
84
70
解释一下:
第一行的n表示我所开的数组大小,即数组为a[1...n],数组元素初始值为0;
C x k 表示在数组元素a[x]每一个加上k,当然k可以是负数,那时候就是减操作了;
Q x y表示查询a[x]+ a[x+1]+...+a[y-1]+a[y]的值。
树状数组除了用于点修改区间查询,还可以进行区间修改点查询,下面是一维区间修改点查询的代码:
#include <cstdio> #define MAX 1000 int c[MAX +1 ]={0}; int n; int lowbit(int x) { return x&(-x); } void change (int x,int k) { while(x>0){ c[x]+=k; x=x-lowbit(x); } } int search(int x) { int s=0; while(x<=n){ s+=c[x]; x=x+lowbit(x); } return s; } int main() { scanf("%d",&n); char s[3]; int x,y,k; while(scanf("%s",&s)!=EOF){ if(s[0]=='C' || s[0]=='c'){ scanf("%d%d%d",&x,&y,&k); change(y,k),change(x-1,-k); } else if(s[0]=='Q' || s[0]=='q'){ scanf("%d",&x); printf("%d\n",search(x)); } } return 0; }
Sample Input
10
C 1 5 5
Q 3
C 3 4 -1
Q 3
4
Q 10
0
C 1 10 4
Q 7
4
Q 3
8
Q 5
9
C 1 2 7
Q 2
16
Q 4
8
Sample Output
5
4
0
4
8
9
16
8
同样也解释一下:
同上第一行的n表示我所开的数组大小
C x y k 表示将数组元素a[x...y]每一个加上k
Q x 表查询a[x]的值。
下面给出二维点修改区间(这时候就是面了)查询和二维区间修改点查询的代码:
二维点修改区间:
#include <cstdio> #include <cstring> #define MAX 100 int c[MAX+1][MAX+1]; int n,m; int lowbit(int x) { return x&(-x); } void change(int x,int y,int k) { while(x<=n){ int yy=y; while(yy<=m){ c[x][yy]=c[x][yy]+k; yy+=lowbit(yy); } x+=lowbit(x); } } int search(int x,int y) { int s=0; while(x>0){ int yy=y; while(yy>0){ s+=c[x][yy]; yy-=lowbit(yy); } x-=lowbit(x); } return s; } int main() { char s[3]; int x1,y1,x2,y2,k; scanf("%d%d",&n,&m); memset(c,0,sizeof(c)); while(scanf("%s",s)!=EOF){ if(s[0]=='C' || s[0]=='c'){ scanf("%d%d%d",&x1,&y1,&k); change(x1,y1,k); } else if(s[0]=='Q' || s[0]=='q'){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2); int s=search(x2,y2)-search(x2,y1-1)-search(x1-1,y2)+search(x1-1,y1-1); printf("%d\n",s); } } return 0; }
二维区间修改点查询:
#include <cstdio> #include <cstring> #define MAX 100 int c[MAX+1][MAX+1]; int n,m; int lowbit(int x) { return x&(-x); } void change(int x,int y,int k) { while(x>0){ int yy=y; while(yy>0){ c[x][yy]=c[x][yy]+k; yy-=lowbit(yy); } x-=lowbit(x); } } int search(int x,int y) { int s=0; while(x<=n){ int yy=y; while(yy<=m){ s+=c[x][yy]; yy+=lowbit(yy); } x+=lowbit(x); } return s; } int main() { char s[3]; int x1,y1,x2,y2,k; scanf("%d%d",&n,&m); memset(c,0,sizeof(c)); while(scanf("%s",s)!=EOF){ if(s[0]=='C' || s[0]=='c'){ scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k); change(x2,y2,k); change(x2,y1-1,-k); change(x1-1,y2,-k); change(x1-1,y1-1,k); } else if(s[0]=='Q' || s[0]=='q'){ scanf("%d%d",&x1,&y1); printf("%d\n",search(x1,y1)); } } return 0; }
前几天做pku2155,先是用线段树,结果TLE……
后面直接套我给出的第四个版,2155 Accepted 4076K 454MS C++ 1190B
在加了点技巧——将二维数组c的类型int改成char,2155 Accepted 1136K 344MS C++ 1150B