树状数组

一个正整数 x 的二进制表示为  ,其中等于1的位是 

则 x 可以被二进制表示为 

不妨设 ,进一步的,区间[1, x] 可以分成 O(logx) 个小区间

这些小区间的共同特点是:若区间结尾为R,则区间长度就是等于R的“二进制分解”下最小的2的次幂,即 lowbit(R).

例如:,区间 [1, 7] 可以分成 [1, 4] [5, 6] [7, 7]

长度分别是 lowbit(4) = 4, lowbit(6) = 2, lowbit(7) = 1 

〔manim | 算法 | 数据结构〕 完全理解并深入应用树状数组 | 支持多种动态维护区间操作_哔哩哔哩_bilibili


树状数组(Binary Indexed Tree)是一种 基于上述思想的数据结构,其基本用途就是维护序列的前缀和。对于给定的序列 a ,我们建立一个数组 c, 其中 c[x] 保存 a 的区间

[x - lowbit(x) + 1, x] 中所有数的和,  


黑色数组代表原来的数组(下面用A[i]代替),红色结构代表我们的树状数组(下面用C[i]代替),发现没有,每个位置只有一个方框,令每个位置存的就是子节点的值的和,则有

  • C[1] = A[1];
  • C[2] = A[1] + A[2];
  • C[3] = A[3];
  • C[4] = A[1] + A[2] + A[3] + A[4];
  • C[5] = A[5];
  • C[6] = A[5] + A[6];
  • C[7] = A[7];
  • C[8] = A[1] + A[2] + A[3] + A[4] + A[5] + A[6] + A[7] + A[8];

可以发现,这颗树是有规律的

C[i] = A[i - 2k+1] + A[i - 2k+2] + ... + A[i];   //k为i的二进制中从最低位到高位连续零的长度


例如i = 8(1000)时候,k = 3,可自行验证。

这个怎么实现求和呢,比如我们要找前7项和,那么应该是SUM = C[7] + C[6] + C[4];

而根据上面的式子,容易得出

其实树状数组就是一个二进制上面的应用。

树状数组(BIT)—— 一篇就够了 - Last_Whisper - 博客园

树状数组详解 - Xenny - 博客园


AcWing 241. 楼兰图腾

输入样例:

5
1 5 3 2 4

输出样例:

3 4

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>using namespace std;const int N = 200010;typedef long long LL;int n;
int a[N];
int tr[N];
int greaterr[N], lower[N];//返回非负整数x在二进制表示下最低位1及其后面的0构成的数值
int lowbit(int x)
{return x & -x;
}//将序列中第x个数加上k。
void add(int x, int c)
{for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}//查询序列前x个数的和
int sum(int x)
{int res = 0;for(int i = x; i; i -= lowbit(i)) res += tr[i];return res;
}int main()
{cin >> n;for(int i = 1; i <= n; i ++ ) scanf("%d",&a[i]);//从左向右,依次统计每个位置左边比第i个数y小的数的个数、以及大的数的个数for(int i = 1; i <= n; i ++ ){int y = a[i];//在前面已加入树状数组的所有数中统计在区间[1, y - 1]的数字的出现次数greaterr[i] = sum(n) - sum(y);//在前面已加入树状数组的所有数中统计在区间[y + 1, n]的数字的出现次数lower[i] = sum(y - 1);//将y加入树状数组,即数字y出现1次add(y,1);}//清空树状数组,从右往左统计每个位置右边比第i个数y小的数的个数、以及大的数的个数memset(tr, 0, sizeof tr);LL res1 = 0, res2 = 0;for(int i = n; i; i --){int y = a[i];res1 += greaterr[i] * (LL)(sum(n) - sum(y));res2 += lower[i] * (LL)(sum(y - 1));//将y加入树状数组,即数字y出现1次add(y,1);}cout << res1 << " " << res2;return 0;
}

AcWing 242. 一个简单的整数问题

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4
Q 1
Q 2
C 1 6 3
Q 2

输出样例:

4
1
2
5

树状数组 + 差分 

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>using namespace std;
typedef long long LL;const int N = 100010;int n, m;
int a[N];
LL tr[N];int lowbit(int x)
{return x & -x;
}void add(int x, int t)
{for(int i = x; i <= n; i += lowbit(i)) tr[i] += t;
}LL sum(int x)
{LL res = 0;for(int i = x; i; i -= lowbit(i)) res += tr[i];return res;
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);for(int i = 1; i <= n; i ++ ) add(i, a[i] - a[i - 1]);while(m -- ){char op[2];int l, r, d;scanf("%s%d", op, &l);if(*op == 'C'){scanf("%d%d", &r, &d);add(l, d), add(r + 1, -d);}else{printf("%lld\n", sum(l));}}return 0;
}

AcWing 243. 一个简单的整数问题2

输入样例:

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出样例:

4
55
9
15

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>using namespace std;typedef long long LL;const int N = 100010;int n,m;
int a[N];
LL tr1[N]; //维护差分数组b[i]的前缀和
LL tr2[N]; //维护b[i] * i 的前缀和int lowbit(int x)
{return x & -x;
}void add(LL tr[], int x, LL c)
{for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}LL sum(LL tr[], int x)
{LL res = 0;for(int i = x; i; i -= lowbit(i)) res += tr[i];return res;
}LL prefix_sum(int x)
{return sum(tr1, x) * (x + 1) - sum(tr2, x);
}int main()
{cin >> n >> m;for(int i = 1; i <= n; i ++ ) scanf("%d",&a[i]);for(int i = 1; i <= n; i ++ ){int b = a[i] - a[i - 1];add(tr1, i, b);add(tr2, i, (LL)i * b);}while(m --){char op[2];int l,r,d;scanf("%s%d%d", op, &l, &r);if(*op == 'Q'){printf("%lld\n",prefix_sum(r) - prefix_sum(l - 1));}else{scanf("%d",&d);add(tr1, l, d), add(tr1, r + 1, -d);add(tr2, l, l * d), add(tr2, r + 1, (r + 1) * -d);}}return 0;
}

AcWing 244. 谜一样的牛

输入样例:

5
1
2
1
0

输出样例:

2
4
5
3
1

// 找到一个最小的x是sum(x) = k
#include <cstring>
#include <iostream>
#include <algorithm>using namespace std;const int N = 100010;int n;
int h[N];
int ans[N];
int tr[N];int lowbit(int x)
{return x & -x;
}void add(int x, int c)
{for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}int sum(int x)
{int res = 0;for(int i = x; i; i -= lowbit(i)) res += tr[i];return res;
}int main()
{cin >> n;for(int i = 2; i <= n; i ++ ) scanf("%d", &h[i]);for(int i = 1; i <= n; i ++ ) tr[i] = lowbit(i);for(int i = n; i; i -- ){int k = h[i] + 1;int l = 1, r = n;while(l < r){int mid = l + r >> 1;if(sum(mid) >= k) r = mid;else l = mid + 1;}ans[i] = r;add(r, -1);}for(int i = 1; i <= n; i ++) printf("%d\n", ans[i]);return 0;
}

【数据结构】—— 树状数组相关推荐

  1. js 数组 实现 完全树_算法和数据结构 | 树状数组(Binary Indexed Tree)

    本文来源于力扣圈子,作者:胡小旭.点击查看原文 力扣​leetcode-cn.com 树状数组或二叉索引树(英语:Binary Indexed Tree),又以其发明者命名为 Fenwick 树.其初 ...

  2. 数据结构——树状数组

    我们今天来讲一个应用比较广泛的数据结构--树状数组 它可以在O(nlogn)的复杂度下进行单点修改区间查询,下面我会分成三个模块对树状数组进行详细的解说,分别是树状数组基本操作.树状数组区间修改单点查 ...

  3. 数据结构--树状数组

    文章目录 1. 树状数组 2. 单点修改 3. 区间修改 4. 完整代码 5. 参考文献 1. 树状数组 类似数据结构:线段树(Segment Tree) 树状数组 跟 线段树 的区别: 树状数组能做 ...

  4. 2017西安交大ACM小学期数据结构 [树状数组 离散化]

    Problem E 发布时间: 2017年6月28日 12:53   最后更新: 2017年6月29日 21:35   时间限制: 1000ms   内存限制: 64M 描述 给定一个长度为n的序列a ...

  5. 2017西安交大ACM小学期数据结构 [树状数组,极大值]

    Problem D 发布时间: 2017年6月28日 10:51   最后更新: 2017年6月28日 16:38   时间限制: 1000ms   内存限制: 32M 描述 给定一个长度为n的序列a ...

  6. 2017西安交大ACM小学期数据结构 [树状数组]

    Problem C 发布时间: 2017年6月28日 11:38   最后更新: 2017年6月28日 16:38   时间限制: 1000ms   内存限制: 32M 描述 给定一个长度为n的序列a ...

  7. 数据结构 —— 树状数组

    [概述] 树状数组又称二叉索引树,常用于高效计算数列的前缀和.区间和,其查询.修改的时间复杂度为 log(n),空间复杂度为 O(n) 树状数组通过将线性结构转化成树状结构,从而进行跳跃式扫描. 优点 ...

  8. Java数据结构-树状数组

    什么是树状数组?[面试5.0] 使用数组表示多叉树的结构,和优先队列有点类似,区别在于优先队列只表示二叉树 主要用来: 更新数组元素的数值并且求数组前K个元素的总和或平均值 时间复杂度为O(logN) ...

  9. “高级”数据结构——树状数组

    数据结构是每一个程序员都必须要学的,非常重要! 大佬的博客写的真好,虽然我看不懂..... 链接http://www.cnblogs.com/RabbitHu/p/BIT.html

  10. 树状数组(单点+区间的所有操作)

    转载:https://blog.csdn.net/I_believe_CWJ/article/details/80374326 更简洁方便的数据结构--树状数组(基于线段树的实现) 1.单点更新+区间 ...

最新文章

  1. noip复赛普及组2020_我校学子在2020年“外研社·国才杯”全国英语写作大赛(高职组)复赛中斩获佳绩...
  2. 第二十六篇 面向对象初识
  3. xp电脑怎么进入bios
  4. java线程安全例子_Java总结篇系列:Java多线程(三)
  5. 密码必须至少为6个字符_1081 检查密码 (15分)
  6. Linq的Distinct方法的扩展
  7. python入门基础系列八_03python—9个基础常识-python小白入门系列
  8. 3种方法实现Android按钮的点击事件,建议收藏!
  9. Full internet technology map. 最全的互联网技术图谱
  10. 【综述】详解ERNIE-Baidu进化史及应用场景
  11. 零基础的人也能学好C++
  12. 暗黑破坏神2 符文自动合成--按键精灵源码
  13. 贴片led极性_贴片发光二极管正负极判断方法详解
  14. 计算机中文件名无法更改原因,电脑系统文件夹名称修改不了怎么办
  15. PowerDesign license安装问题
  16. 国立台湾大学里的自动化书库(立体库)
  17. 升级工作环境并支持C++17
  18. java制作小鱼吃大鱼_大鱼吃小鱼游戏(Java编写)
  19. js 按拼音 首字母 排序 并分组
  20. Python之ffmpeg:利用python编程基于ffmpeg将m4a格式音频文件转为mp3格式文件

热门文章

  1. 愚人节,这样的微信公众号图文排版方式你见过吗?
  2. Arcgis中修改属性字段名
  3. 《Solar Energy Materials and Solar Cells》期刊介绍(SCI 2区)
  4. 自动登录163邮箱的批处理
  5. 无线网卡代理服务器连接失败怎么办,电脑用无线网卡连不上网的解决方法
  6. 睡眠伤害计算机硬件吗,电脑高手告诉您,电脑不关机只睡眠到底伤不伤硬盘?...
  7. 自己制作深度学习数据集教程
  8. Calendar类你可能不知道的坑
  9. react-native系列(11)组件篇:Image图片加载和ImageEditor图片剪切
  10. 当代考研人的发疯行为!笑到打鸣哈哈嗝哈哈嗝!