当前位置:   article > 正文

树状数组_树状数组的时间复杂度

树状数组的时间复杂度
1.平常我们会遇到一些对数组进行维护查询的操作,比较常见的如,
①修改某点的值
②求某个区间的和


2.当数据规模不大的时候,
①对于修改某点的值是非常容易的,时间复杂度为 O(1)
②求一个区间和就要从start循环到end,时间复杂度为 O(n)
③如果实时的对数组进行 M次修改或求和,最坏的情况下
时间复杂度为 O(M*N),当规模增大后,必定超时


3.树状数组的时间复杂度为 O(M*logN)  


4. A数组就是我们要维护和查询的数组,其实整个过程中根本用不到,
C数组才是重点,C数组的构造规则为: 
C8=C4+C6+C7+A8,C6=C5+A6,C4=C2+C3+A4,C2=C1+A2...
C8表示 A1~A8的和,C6表示 A5~A6的和, 
C8可以看做左半边的和 A1~A4 + 右半边的和 A5~A8,
左半边的和确定为 C4,右半边同样的规则把A5~A8一分为二,
继续下去都是一分为二,直到不能再分


5.树状数组的基础就是一个被构造出来的式子:c[i]=a[i]+a[i-1]+...+a[i-2^k+1];

k代表二进制的最后连续 0的个数,   比如,对于1000和101000,k=3


6.lowbit(k)就是把 k的二进制的高位1全部清空,只留下最低位的 1,
比如,10的二进制数是1010,则lowbit(10)=lowbit(1010)=0010;


7.实现方法 lowbit(k)=k&-k,
一个数加一个负号就是把这个数的二进制取反加一 
如 -10的二进制就是 -1010=0101+1=0110,然后 1010 & 0110=0010
 
8.节点与其子树的关系,i=j+lowbit(j),lowbit是 j的最低位 1所代表

的数字,可以把子区间的变化以 log 2n的次数传递上去 

  1. int lowbit(int i)
  2. {
  3. return i&(-i);
  4. }
  5. void add(int i,int value)
  6. {
  7. while(i<=N){
  8. c[i]+=value;
  9. i+=lowbit(i);
  10. }
  11. }
  12. int sum(int i){
  13. int sum=0;
  14. while(i>0){
  15. sum+=c[i];
  16. i-=lowbit(i);
  17. }
  18. return sum;
  19. }

总结:

树状数组(Binary Indexed Tree)

1.解决动态前缀和问题的数据结构

2.查询区间和,例如区间和[5,8]=sum(8)-sum(4); 

3.解决区间加/单点查询问题 
例如:给区间[3,6]所有元素加一个数5,
执行的操作是add(3,5),add(6+1,-5); 
然后查询3,4,5,6点的值,比原来大5
 
4.二维树状数组
单点修改+二维前缀和sum(x,y)(即子矩阵元素之和) 

例题:
poj2352 二维偏序问题
bzoj 1878,2743,1452
Codeforces Round #755D
题解:

www.cnblogs.com/autsky-jadek/ 


  1. const int maxn=305;
  2. struct BIT_2D{
  3. int d[maxn][maxn];
  4. void update(int x,const int &y,const int &v){
  5. for(;x<=n;x+=(x&(-x)))
  6. for(int j=y;j<=n;j+=(j&(-j)))
  7. d[x][j]+=v;
  8. }
  9. int getsum(int x,const int &y)
  10. {
  11. int res=0;
  12. for(;x;x-=(x&(-x)))
  13. for(int j=y;j;j-=(j&(-j)))
  14. res+=d[x][j];
  15. return res;
  16. }
  17. };

模板题目:HDU 1166  

点击打开链接

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int N,c[50005];
  4. int lowbit(int i)
  5. {
  6. return i&(-i);
  7. }
  8. void add(int i,int value)
  9. {
  10. while(i<=N){
  11. c[i]+=value;
  12. i+=lowbit(i);
  13. }
  14. }
  15. int sum(int i){
  16. int sum=0;
  17. while(i>0){
  18. sum+=c[i];
  19. i-=lowbit(i);
  20. }
  21. return sum;
  22. }
  23. int main()
  24. {
  25. int t,kase=1,d;
  26. scanf("%d",&t);
  27. while(t--)
  28. {
  29. printf("Case %d:\n",kase++);
  30. scanf("%d",&N);
  31. memset(c,0,sizeof(c));
  32. for(int i=1;i<=N;i++){
  33. scanf("%d",&d);
  34. add(i,d);
  35. }
  36. char command[15];int a,b;
  37. while(~scanf("%s",command)&&command[0]!='E'){
  38. //getchar();
  39. scanf("%d%d",&a,&b);
  40. if(command[0]=='Q')
  41. //c[1]~c[b]的和为sum(b),c[1]~c[a-1]的和为sum(a-1)
  42. //c[a]~c[b]的和为sum(b)-sum(a-1)
  43. printf("%d\n",sum(b)-sum(a-1));
  44. if(command[0]=='A')
  45. add(a,b);
  46. if(command[0]=='S')
  47. add(a,-b);
  48. }
  49. }
  50. return 0;
  51. }

例一:bzoj1878

点击打开链接

1878: [SDOI2009]HH的项链

Time Limit: 4 Sec   Memory Limit: 64 MB
Submit: 5674   Solved: 2807
[ Submit][ Status][ Discuss]

Description

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一
段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此他的项链变得越来越长。有一天,他突然提出了一
个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只
好求助睿智的你,来解决这个问题。

Input

第一行:一个整数N,表示项链的长度。 
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为0到1000000之间的整数)。 
第三行:一个整数M,表示HH询问的个数。 
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
N ≤ 50000,M ≤ 200000。

Output

M行,每行一个整数,依次表示询问对应的答案。

Sample Input

6
1 2 3 4 3 5
3
1 2
3 5
2 6

Sample Output

2
2
4


题意:
给一个n个数的序列,m个询问,每次询问一个区间内不同的数的个数 


思路:
每个数都可以预处理它上一次出现在什么位置,
每次添加元素的时候,将它上一次出现的位置+1的地方加1,
然后在其本身位置+1的值打上个-1标记,这样就成了线性求值,
也就是每次求getsum(a[i].l)。


所有区间按右端点排序 对于每一个[l,r]可以这样处理: 

l~r中的数的上一次出现的位置在l左边的数的个数


  1. #include <stdio.h>
  2. #include <algorithm>
  3. using namespace std ;
  4. struct node
  5. {
  6. int l ;
  7. int r ;
  8. int id ;
  9. };
  10. node a[200100] ;
  11. int pre[200100] ;
  12. int col[200100] ;
  13. int c[200100] ;
  14. int ans[200100] ;
  15. int last[1000100] ;
  16. int n;
  17. int lowbit(int x)
  18. {
  19. return x&(-x) ;
  20. }
  21. void update(int x , int p)
  22. {
  23. while(x <= n)
  24. {
  25. c[x] += p ;
  26. x += lowbit(x) ;
  27. }
  28. }
  29. int getsum(int x)
  30. {
  31. int sum = 0 ;
  32. while(x)
  33. {
  34. sum += c[x] ;
  35. x -= lowbit(x) ;
  36. }
  37. return sum ;
  38. }
  39. int cmp(node asdf, node b)
  40. {
  41. return asdf.r < b.r;
  42. }
  43. int main()
  44. {
  45. scanf("%d" , &n) ;
  46. for(int i = 1 ; i <= n ; i++)
  47. {
  48. scanf("%d" , &col[i]) ;
  49. pre[i] = last[col[i]] ;
  50. last[col[i]] = i ;
  51. }
  52. int m ;
  53. scanf("%d" , &m) ;
  54. for(int i = 1 ; i <= m ;i++)
  55. {
  56. scanf("%d%d" , &a[i].l , &a[i].r) ;
  57. a[i].id = i ;
  58. }
  59. sort(a + 1 , a + 1 + m , cmp) ;
  60. int now = 0 ;
  61. for(int i = 1 ; i <= m ; i++)
  62. {
  63. while(now < a[i].r)
  64. {
  65. now ++ ;
  66. update(pre[now] + 1 , 1) ;
  67. if(now != n){update(now + 1 ,-1) ;}
  68. }
  69. ans[a[i].id] = getsum(a[i].l) ;
  70. }
  71. for(int i = 1 ; i <= m ; i++)
  72. {
  73. printf("%d\n" , ans[i]) ;
  74. }
  75. }
poj2352 Stars 树状数组 
题意: 
给出N个星星的坐标(包括0),已经先按y递增,若y相等,x递增排序,
每个星星都有一个等级,规定它的等级就是在它左下方的星星的个数
依次输出0到n-1的星星的个数


思路:
树状数组最基本的应用,统计横坐标为x的星星前面比它小的星星的个数
注意:

1.树状数组下标为0的位置不可用,所以在输入坐标时加1 

  1. #include<iostream>
  2. #include<string>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<algorithm>
  6. using namespace std;
  7. #define CLR(arr, val) memset(arr, val, sizeof(arr))
  8. #define N 32010//0<=X,Y<=32000
  9. int n;
  10. int lev[N], c[N];
  11. int lowbit(int x)
  12. {
  13. return x & (-x);
  14. }
  15. void add(int i, int data)
  16. {
  17. while(i < N)//这里应该特别注意,是x的范围,而不是数据量n
  18. {
  19. c[i] += data;
  20. i += lowbit(i);
  21. }
  22. }
  23. int getsum(int x)
  24. {
  25. int res = 0;
  26. while(x > 0)
  27. {
  28. res += c[x];
  29. x -= lowbit(x);
  30. }
  31. return res;
  32. }
  33. int main()
  34. {
  35. int x, y;
  36. while(~scanf("%d", &n))
  37. {
  38. CLR(lev, 0); CLR(c, 0);
  39. for(int i = 0; i < n; ++i)
  40. {
  41. scanf("%d%d", &x, &y);
  42. x++; //有0出现,树状数组无法处理。故+1
  43. lev[getsum(x)]++; //先统计,不包括本身
  44. add(x, 1); //加入
  45. }
  46. for(int i = 0; i < n; ++i)
  47. printf("%d\n", lev[i]);
  48. }
  49. return 0;
  50. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/529132
推荐阅读
相关标签
  

闽ICP备14008679号