• (笔记整理未完成)【数据结构】树状数组


    “有一些树状数组高级操作,能干很多事情,但可能也比较复杂,线段树常数大但是完全包含树状数组,所以很多问题能用树状数组解决的同样也可以用线段树解决。”

    知识点


    一 . 定义

    树状数组,也称二叉索引树(英语:Binary Indexed Tree),又以其发明者命名为Fenwick树,其最初是解决数据压缩里的累积频率(Cumulative Frequency)的计算问题,现多用于高效计算数列的前缀和, 区间和

    原理:(待填坑)


    模板题

    1 . 基础版:洛谷 P3374 【模板】树状数组 1

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    • 将某一个数加上 x

    • 求出某区间每一个数的和

    输入格式

    第一行包含两个正整数 n,m,分别表示该数列数字的个数和操作的总个数。

    第二行包含 n 个用空格分隔的整数,其中第 ii 个数字表示数列第 i 项的初始值。

    接下来 m 行每行包含 3 个整数,表示一个操作,具体如下:

    • 1 x k 含义:将第 x 个数加上 k

    • 2 x y 含义:输出区间 [x,y] 内每个数的和

    输出格式

    输出包含若干行整数,即为所有操作 2 的结果。

    输入输出样例

    输入 #1

    5 5
    1 5 4 2 3
    1 1 3
    2 2 5
    1 3 -1
    1 4 2
    2 1 4

    输出 #1

    14
    16

    说明/提示

    【数据范围】

    对于 30% 的数据,1 \leqslant n \leqslant 81\leqslant m \leqslant 10
    对于 70% 的数据,1\leqslant n,m \leqslant 10^4
    对于 100% 的数据,1\leqslant n,m \leqslant 5\times 10^5

    样例说明:

    故输出结果14、16

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int maxn=1e8+5;
    7. int a[maxn];
    8. int n,m;
    9. int lowbit(int x) //x的二进制表达式最右边的1的值
    10. {
    11. return x & -x; //计算机的补码表示,-x表示x按位取反
    12. }
    13. void add(int x,int y) //区间修改;
    14. {
    15. while(x<=n)
    16. {
    17. a[x]+=y;
    18. x+=lowbit(x);
    19. }
    20. }
    21. int sum(int x)
    22. {
    23. int result=0;
    24. while (x>0)
    25. {
    26. result+=a[x];
    27. x-=lowbit(x);
    28. }
    29. return result;
    30. }
    31. int main()
    32. {
    33. cin>>n>>m;
    34. for(int i=1;i<=n;++i)
    35. {
    36. int p;
    37. cin>>p;
    38. add(i,p);
    39. }
    40. for(int i=1;i<=m;++i)
    41. {
    42. int num,x,y;
    43. cin>>num>>x>>y;
    44. if(num==1)
    45. {
    46. add(x,y);
    47. }
    48. if(num==2)
    49. {
    50. cout<<sum(y)-sum(x-1)<
    51. }
    52. }
    53. return 0;
    54. }

     2 . 增强版:洛谷 P3368 【模板】树状数组 2

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1. 将某区间每一个数加上 x;

    2. 求出某一个数的值。

    输入格式

    第一行包含两个整数 N、M,分别表示该数列数字的个数和操作的总个数。

    第二行包含 N 个用空格分隔的整数,其中第 ii 个数字表示数列第 i 项的初始值。

    接下来 M 行每行包含 2 或 4个整数,表示一个操作,具体如下:

    操作 1: 格式:1 x y k 含义:将区间 [x,y] 内每个数加上 k;

    操作 2: 格式:2 x 含义:输出第 x 个数的值。

    输出格式

    输出包含若干行整数,即为所有操作 2 的结果。

    输入输出样例

    输入 #1

    5 5
    1 5 4 2 3
    1 2 4 2
    2 3
    1 1 5 -1
    1 3 5 7
    2 4

    输出 #1

    6
    10

    说明/提示

    样例 1 解释:

    故输出结果为 6、10。

    数据规模与约定

    对于 30% 的数据:N \leqslant 8M \leqslant 10

    对于 70% 的数据:N \leqslant 10000M \leqslant 10000

    对于 100% 的数据:1 \leqslant N, M\leqslant 5000001 \leqslant x, y \leqslant n,保证任意时刻序列中任意元素的绝对值都不大于 2^{30}


     1 . 使用暴力

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int maxn=1e8+5;
    7. int a[maxn];
    8. int main()
    9. {
    10. int n,m;
    11. cin>>n>>m; //n,m的数据范围可达到
    12. for(int i=1;i<=n;++i)
    13. {
    14. cin>>a[i];
    15. }
    16. for(int i=1;i<=m;++i)
    17. {
    18. int num,x,y,k;
    19. cin>>num;
    20. if(num==1)
    21. {
    22. cin>>x>>y>>k;
    23. for(int j=x;j<=y;++j) //若区间数x,y特别大,所需时间长
    24. {
    25. a[j]+=k;
    26. }
    27. }
    28. else if(num==2)
    29. {
    30. cin>>x;
    31. cout<
    32. }
    33. }
    34. return 0;
    35. }

     2 . 使用树状数组+差分优化

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. const int maxn=1e8+5;
    7. int a[maxn],b[maxn];
    8. int n,m;
    9. int lowbit(int x)
    10. {
    11. return x & -x;
    12. }
    13. void add(int x,int y)
    14. {
    15. while(x<=n)
    16. {
    17. b[x]+=y;
    18. x+=lowbit(x);
    19. }
    20. }
    21. int sum(int x)
    22. {
    23. int result=0;
    24. while(x>0)
    25. {
    26. result+=b[x];
    27. x-=lowbit(x);
    28. }
    29. return result;
    30. }
    31. int main()
    32. {
    33. cin>>n>>m;
    34. for(int i=1;i<=n;++i)
    35. {
    36. cin>>a[i];
    37. add(i,a[i]-a[i-1]);
    38. //a[i]-a[i-1]求得差分数组;
    39. }
    40. for(int j=1;j<=m;++j)
    41. {
    42. int num;
    43. cin>>num;
    44. if(num==1)
    45. {
    46. int x,y,k;
    47. cin>>x>>y>>k;
    48. add(x,k); //等价于b[x]+=z;即从x以后的数都加上k
    49. add(y+1,-k); //b[y+1]-=z;即从y+1后恢复原来的数
    50. //这两个式子是差分的精髓
    51. }
    52. else if(num==2)
    53. {
    54. int x;
    55. cin>>x;
    56. cout<<sum(x)<
    57. }
    58. }
    59. return 0;
    60. }

  • 相关阅读:
    2022爱分析・采购数字化厂商全景报告 | 爱分析报告
    企业全面云化的时代——云数据库的未来
    Springboot+Mybatis处理复杂数据类型(Map、List等)
    零知识证明与同态加密:隐私计算的双剑
    【Vue面试题十六】、Vue.observable你有了解过吗?说说看
    Ubuntu20.04 安装 Matlab R2021a
    视频分段方法:视频批量处理与音频提取的操作解析
    Mysql架构解析,InnoDB架构概述。
    QR.js JS 生成 PNG二维码图片,使用说明
    ESP32基础应用之LVGL基础
  • 原文地址:https://blog.csdn.net/gzkeylucky/article/details/126328442