树状数组

我不是女神ヾ 2023-08-17 16:26 216阅读 0赞

详细介绍:https://www.cnblogs.com/xenny/p/9739600.html

http://www.sohu.com/a/245746824_100201031

https://www.cnblogs.com/wkfvawl/p/9445376.html

单点更新、单点查询

传统数组可做

1.单点修改+区间查询

  1. int n;//点的数量
  2. int a[50005],c[50005]; //对应原数组和树状数组
  3. int lowbit(int x){
  4. return x&(-x);
  5. }
  6. void updata(int i,int k){ //在i位置加上k
  7. while(i <= n){
  8. c[i] += k;
  9. i += lowbit(i);
  10. }
  11. }
  12. //求sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1)
  13. int getsum(int i){
  14. int res = 0;
  15. while(i > 0){
  16. res += c[i];
  17. i -= lowbit(i);
  18. }
  19. return res;
  20. }
  21. int main()
  22. {

memset(a,0,sizeof(a));
memset(c,0,sizeof(c));

}

模板题:

ContractedBlock.gif ExpandedBlockStart.gif

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<algorithm>
  6. //单点修改加区间查询
  7. using namespace std;
  8. int n;
  9. int a[50005],c[50005]; //对应原数组和树状数组
  10. int lowbit(int x){
  11. return x&(-x);
  12. }
  13. void updata(int i,int k){ //在i位置加上k
  14. while(i <= n){
  15. c[i] += k;
  16. i += lowbit(i);
  17. }
  18. }
  19. //求sum(x)就是a[x]的前缀和,想查询l~r区间的元素和只需要求出来sum(r)-sum(l-1)
  20. int getsum(int i){
  21. int res = 0;
  22. while(i > 0){
  23. res += c[i];
  24. i -= lowbit(i);
  25. }
  26. return res;
  27. }
  28. int main()
  29. {
  30. int T;
  31. scanf("%d",&T);
  32. for(int i=1;i<=T;i++)
  33. {
  34. scanf("%d",&n);
  35. memset(a, 0, sizeof a);
  36. memset(c, 0, sizeof c);
  37. for(int i=1;i<=n;i++)
  38. {
  39. scanf("%d",&a[i]);
  40. updata(i,a[i]);
  41. }
  42. printf("Case %d:\n",i);
  43. char s[10];
  44. while(cin>>s){
  45. if(s[0]=='Q'){
  46. //区间查询
  47. int l,r;
  48. scanf("%d%d",&l,&r);
  49. cout<<getsum(r)-getsum(l-1)<<endl;
  50. }
  51. if(s[0]=='A'){
  52. //单点增加 第x个数加t
  53. int x,t;
  54. scanf("%d%d",&x,&t);
  55. updata(x,t);
  56. }
  57. if(s[0]=='S'){
  58. //单点减少 第x个数减t
  59. int x,t;
  60. scanf("%d%d",&x,&t);
  61. updata(x,-1*t);
  62. }
  63. if(s[0]=='E')//结束
  64. break;
  65. }
  66. }
  67. }

HDU - 1166

2.区间修改+单点查询

  1. int n,m;
  2. int a[50005] = {
  3. 0},c[50005]; //对应原数组和树状数组
  4. int lowbit(int x){
  5. return x&(-x);
  6. }
  7. void updata(int i,int k){ //在i位置加上k
  8. while(i <= n){
  9. c[i] += k;
  10. i += lowbit(i);
  11. }
  12. }
  13. int getsum(int i){ //求D[1 - i]的和,即A[i]值
  14. int res = 0;
  15. while(i > 0){
  16. res += c[i];
  17. i -= lowbit(i);
  18. }
  19. return res;
  20. }
  21. int main(){
  22. cin>>n;
  23. memset(a,0,sizeof(a));
  24. memset(c,0,sizeof(c));
  25. for(int i = 1; i <= n; i++){
  26. cin>>a[i];
  27. updata(i,a[i] - a[i-1]); //输入初值的时候,也相当于更新了值
  28. }
  29. //[x,y]区间内加上k
  30. updata(x,k); //A[x] - A[x-1]增加k
  31. updata(y+1,-k); //A[y+1] - A[y]减少k
  32. //查询i位置的值
  33. int sum = getsum(i);
  34. return 0;
  35. }

例题:P3368 【模板】树状数组 2 https://www.luogu.org/problem/show?pid=3368

ContractedBlock.gif ExpandedBlockStart.gif

  1. 1 #include<iostream>
  2. 2 #include<cstdio>
  3. 3 #include<cmath>
  4. 4 #include<cstring>
  5. 5 #include<algorithm>
  6. 6 //单点修改加区间查询
  7. 7 using namespace std;
  8. 8 int n,m;
  9. 9 int a[500005] = {
  10. 0},c[500005]; //对应原数组和树状数组
  11. 10
  12. 11 int lowbit(int x){
  13. 12 return x&(-x);
  14. 13 }
  15. 14
  16. 15 void updata(int i,int k){ //在i位置加上k
  17. 16 while(i <= n){
  18. 17 c[i] += k;
  19. 18 i += lowbit(i);
  20. 19 }
  21. 20 }
  22. 21
  23. 22 int getsum(int i){ //求D[1 - i]的和,即A[i]值
  24. 23 int res = 0;
  25. 24 while(i > 0){
  26. 25 res += c[i];
  27. 26 i -= lowbit(i);
  28. 27 }
  29. 28 return res;
  30. 29 }
  31. 30
  32. 31 int main(){
  33. 32 int m;
  34. 33 cin>>n>>m;
  35. 34 memset(a,0,sizeof(a));
  36. 35 memset(c,0,sizeof(c));
  37. 36 for(int i = 1; i <= n; i++){
  38. 37 cin>>a[i];
  39. 38 updata(i,a[i] - a[i-1]); //输入初值的时候,也相当于更新了值
  40. 39 }
  41. 40 for(int i=1;i<=m;i++)
  42. 41 {
  43. 42 int t;
  44. 43 cin>>t;
  45. 44 if(t==1)
  46. 45 {
  47. 46 int x,y,k;
  48. 47 cin>>x>>y>>k;
  49. 48 //[x,y]区间内加上k
  50. 49 updata(x,k); //A[x] - A[x-1]增加k
  51. 50 updata(y+1,-k); //A[y+1] - A[y]减少k
  52. 51 }
  53. 52 else
  54. 53 {
  55. 54 int kk;
  56. 55 cin>>kk;
  57. 56 //查询i位置的值
  58. 57 int sum = getsum(kk);
  59. 58 cout<<sum<<endl;
  60. 59 }
  61. 60 }
  62. 61 return 0;
  63. 62 }

3.区间修改+区间查询

  1. int n,m;
  2. int a[50005] = {
  3. 0};
  4. int sum1[50005]; //(D[1] + D[2] + ... + D[n])
  5. int sum2[50005]; //(1*D[1] + 2*D[2] + ... + n*D[n])
  6. int lowbit(int x){
  7. return x&(-x);
  8. }
  9. void updata(int i,int k){
  10. int x = i; //因为x不变,所以得先保存i值
  11. while(i <= n){
  12. sum1[i] += k;
  13. sum2[i] += k * (x-1);
  14. i += lowbit(i);
  15. }
  16. }
  17. int getsum(int i){ //求前缀和
  18. int res = 0, x = i;
  19. while(i > 0){
  20. res += x * sum1[i] - sum2[i];
  21. i -= lowbit(i);
  22. }
  23. return res;
  24. }
  25. int main(){
  26. cin>>n;
  27. for(int i = 1; i <= n; i++){
  28. cin>>a[i];
  29. updata(i,a[i] - a[i-1]); //输入初值的时候,也相当于更新了值
  30. }
  31. //[x,y]区间内加上k
  32. updata(x,k); //A[x] - A[x-1]增加k
  33. updata(y+1,-k); //A[y+1] - A[y]减少k
  34. //求[x,y]区间和
  35. int sum = getsum(y) - getsum(x-1);
  36. return 0;
  37. }

例题:https://vjudge.net/problem/POJ-3468

ContractedBlock.gif ExpandedBlockStart.gif

  1. 1 #include<iostream>
  2. 2 #include<cstdio>
  3. 3 #include<cmath>
  4. 4 #include<cstring>
  5. 5 #include<algorithm>
  6. 6 //单点修改加区间查询
  7. 7 using namespace std;
  8. 8 #define ll long long
  9. 9 int n,m;
  10. 10 ll a[100005] = {
  11. 0};
  12. 11 ll sum1[100005]; //(D[1] + D[2] + ... + D[n])
  13. 12 ll sum2[100005]; //(1*D[1] + 2*D[2] + ... + n*D[n])
  14. 13
  15. 14 int lowbit(int x){
  16. 15 return x&(-x);
  17. 16 }
  18. 17
  19. 18 void updata(int i,ll k){
  20. 19 int x = i; //因为x不变,所以得先保存i值
  21. 20 while(i <= n){
  22. 21 sum1[i] += k;
  23. 22 sum2[i] += k * (x-1);
  24. 23 i += lowbit(i);
  25. 24 }
  26. 25 }
  27. 26
  28. 27 ll getsum(int i){ //求前缀和
  29. 28 ll res = 0, x = i;
  30. 29 while(i > 0){
  31. 30 res += x * sum1[i] - sum2[i];
  32. 31 i -= lowbit(i);
  33. 32 }
  34. 33 return res;
  35. 34 }
  36. 35
  37. 36 int main(){
  38. 37 int Q;
  39. 38 ios_base::sync_with_stdio(false);
  40. 39 cin.tie(0);
  41. 40 cout.tie(0);
  42. 41 cin>>n>>Q;
  43. 42 for(int i = 1; i <= n; i++){
  44. 43 cin>>a[i];
  45. 44 updata(i,a[i] - a[i-1]); //输入初值的时候,也相当于更新了值
  46. 45 }
  47. 46
  48. 47 for(int i=1;i<=Q;i++)
  49. 48 {
  50. 49 char c;
  51. 50 cin>>c;
  52. 51 if(c=='Q')
  53. 52 {
  54. 53 int x,y;
  55. 54 cin>>x>>y;
  56. 55 long long sum = getsum(y) - getsum(x-1);
  57. 56 cout<<sum<<endl;
  58. 57 }
  59. 58 else
  60. 59 {
  61. 60 int x,y;
  62. 61 ll k;
  63. 62 cin>>x>>y>>k;
  64. 63 //[x,y]区间内加上k
  65. 64 updata(x,k); //A[x] - A[x-1]增加k
  66. 65 updata(y+1,-k); //A[y+1] - A[y]减少k
  67. 66 }
  68. 67 }
  69. 68 return 0;
  70. 69 }

转载于:https://www.cnblogs.com/Aiahtwo/p/11331107.html

发表评论

表情:
评论列表 (有 0 条评论,216人围观)

还没有评论,来说两句吧...

相关阅读

    相关 树状数组

    目录: 1. 概念 2. 应用 3. 充分性 4. 建立树状数组 5. 典题分析   一.概念 数组来模拟树形结构。那么、为什么不直接建树?没必要,因为树

    相关 树状数组

    1、概述 树状数组(binary indexed tree),是一种设计新颖的数组结构,它能够高效地获取数组中连续n个数的和。概括说,树状数组通常用于解决以下问题:数组\{a

    相关 树状数组

    树状数组 树状数组:用线性数据结构的方法解决动态统计子树权和的问题。 类似于线段树,将区间分成小段,方便计算权和。 举个栗子,将a数组构造成树状数组c。 如果a

    相关 树状数组初探

    前言 在前一篇文章:[线段树初探][Link 1] 中我们看了一下线段树的基本思想并且知道了线段树擅长于解决区间问题。其实对于某些区间问题,我们不仅可以用线段树解决,还可

    相关 树状数组实现

    树状数组能够完成如下操作: 给一个序列a0-an 计算前i项和 对某个值加x 时间o(logn) 注意:有人觉得前缀和就行了,但是你还要维护啊,改变某个值,一个一个改