• 高级数据结构——树状数组


            树状数组(Binary Index Tree, BIT),是一种一般用来处理单点修改区间求和操作类型的题目的数据结构,时间复杂度为O(log n)。

            对于普通数组来说,单点修改的时间复杂度是 O(1),但区间求和的时间复杂度是 O(n) 。如果使用前缀和数组呢?区间求和的时间复杂度降低为O(1),但是单点修改又会变为O(n) 。那么,我们能不能找到一种数组,中和两者的时间复杂度都不那么高?

            树状数组就是这么一种结构,它通过二进制来划分区间,例如我们要求出前13项的和,13的二进制表示为(1101),接着分别查询((0000), (1000)],((1000), (1100)],和((1100), (1101)]的和并相加。

            上述区间的划分规则是将13不断减去最低位的1来划分的,这里就需要用的之前学过的lowbit(),acwing基础课——位运算_acwing位运算_我的鱼干呢w的博客-CSDN博客可从这里学习lowbit()的用法和实现。

            树状数组的大致结构如下:

            通过树状数组,我们需要更新的区间至多不会超过log~2~N,这样我们就能以O(log n)的时间复杂度完成单点修改和区间查询。下面给出树状数组的实现:

    1. //单点修改
    2. void modify(int k, int x)
    3. {
    4. for(int i = k; i <= n; i += lowbit(i)) tr[i] += x;
    5. }
    1. //统计前x项的和
    2. int count(int x)
    3. {
    4. int res = 0;
    5. for(int i = x; i; i -= lowbit(i)) res += tr[i];
    6. return res;
    7. }
    8. //区间求和
    9. int query(int a, int b)
    10. {
    11. return count(b) - count(a - 1);
    12. }

    来到例题练练手吧!

    241. 楼兰图腾 - AcWing题库

    在完成了分配任务之后,西部 314 来到了楼兰古城的西部。

    相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(),他们分别用 V 和  的形状来代表各自部落的图腾。

    西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。

    西部 314 认为这幅壁画所包含的信息与这 n 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn)其中 y1∼yn 是 1 到 n 的一个排列。

    西部 314 打算研究这幅壁画中包含着多少个图腾。

    如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤iyj,yjV 图腾;

    如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤iy,则称这三个点构成  图腾;

    西部 314 想知道,这 n 个点中两个部落图腾的数目。

    因此,你需要编写一个程序来求出 V 的个数和  的个数。

    输入格式

    第一行一个数 n。

    第二行是 n 个数,分别代表 y1,y2,…,yn。

    输出格式

    两个数,中间用空格隔开,依次为 V 的个数和  的个数。

    数据范围

    对于所有数据,n≤200000,且输出答案不会超过 int64。
    y1∼yn 是 1 到 n 的一个排列。

    输入样例:
    1. 5
    2. 1 5 3 2 4
    输出样例:
    3 4
    1. #include
    2. #include
    3. #include
    4. using namespace std;
    5. typedef long long LL;
    6. const int N = 200010;
    7. int n;
    8. int a[N];
    9. int tr[N];
    10. int Greator[N], Lower[N];
    11. int lowbit(int x)
    12. {
    13. return x & -x;
    14. }
    15. void add(int k, int x)
    16. {
    17. for(int i = k; i <= n; i += lowbit(i)) tr[i] += x;
    18. }
    19. int sum(int x)
    20. {
    21. int res = 0;
    22. for(int i = x; i; i -= lowbit(i)) res += tr[i];
    23. return res;
    24. }
    25. int main()
    26. {
    27. cin >> n;
    28. for(int i = 1; i <= n; i ++ ) cin >> a[i];
    29. for(int i = 1; i <= n; i ++ )
    30. {
    31. int u = a[i];
    32. Greator[i] = sum(n) - sum(u);
    33. Lower[i] = sum(u - 1);
    34. add(u, 1);
    35. }
    36. memset(tr, 0, sizeof tr);
    37. LL res1 = 0, res2 = 0;
    38. for(int i = n; i; i -- )
    39. {
    40. int u = a[i];
    41. res1 += (LL)Greator[i] * (sum(n) - sum(u));
    42. res2 += (LL)Lower[i] * sum(u - 1);
    43. add(u, 1);
    44. }
    45. cout << res1 << " " << res2;
    46. return 0;
    47. }
  • 相关阅读:
    flask SQLAlchemy
    Python变量
    『力扣刷题本』:链表分割
    Qt 序列化函数和反序列化函数
    10个问题说清楚 什么是元宇宙 - 十问元宇宙:如何将抽象的概念具象化?
    Linux内核编译-ubuntu22.03-Linux-6.6
    Sublime Text 最简单的更换主题和字体颜色的办法
    net基于asp.net的二手商品的交易系统-二手网站-计算机毕业设计
    学习Java语法糖这一篇就够了(详细版)
    Avalonia for VSCode
  • 原文地址:https://blog.csdn.net/qq_62417282/article/details/134378620