树型结构——树状数组
目录
1.树状数组的引入
2.基本操作:主要包括 插入操作,查询操作.
3.具体实现:
例题:1.楼兰图腾
241. 楼兰图腾
2.一个简单的整数问题
3.一个简单的整数问题2
1.树状数组的引入
树状数组是用来解决区间修改-单点查询以及单点修改-区间查询和区间修改-区间查询问题的一种优化时间的算法。
这里推荐B站视频歪瑞古德:〔manim | 算法 | 数据结构〕 完全理解并深入应用树状数组 | 支持多种动态维护区间操作_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1pE41197Qj?from=search&seid=13866619238299900297&spm_id_from=333.337.0.0
简单来说,如果一个问题能转变成上述三个问题,就可以使用树状数组,将修改和查询操作的时间复杂度从O(n)优化到O(log n),树状数组是一种简单的数据结构。
2.基本操作:主要包括 插入操作,查询操作.
3.具体实现:
这里以区间求和问题作为例子。
也就是区间修改,区间求和
插入操作:
void add(int tr[],int x,int c){for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=c;
}
查询操作:
int sum(int tr[],int x){int res=0;for(int i=x;i;i-=lowbit(i)) res+=tr[i];return res;
}
例题:1.楼兰图腾
如何把一个问题转换成区间修改,区间查询,是解决树状数组问题的关键,另外能用树状数组解决的问题也可以用线段树解决。
241. 楼兰图腾
- 题目
https://www.acwing.com/problem/content/description/243/
- 提交记录
- 讨论
- 题解
- 视频讲解
在完成了分配任务之后,西部 314 来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V
),一个部落崇拜铁锹(∧
),他们分别用 V
和 ∧
的形状来代表各自部落的图腾。
西部 314 在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n 个点,经测量发现这 n 个点的水平位置和竖直位置是两两不同的。
西部 314 认为这幅壁画所包含的信息与这 nn 个点的相对位置有关,因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn),其中 y1∼yn是 1 到 n 的一个排列。
西部 314314 打算研究这幅壁画中包含着多少个图腾。
如果三个点 (i,yi),(j,yj),(k,yk) 满足 1≤i<j<k≤n且 yi>yj则称这三个点构成 V
图腾;
如果三个点 (i,yi),(j,yj),(k,yk)满足 1≤i<j<k≤n且 yi<yj,则称这三个点构成 ∧
图腾;
西部 314想知道,这 n个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 V
的个数和 ∧
的个数。
输入格式
第一行一个数 n。
第二行是 n 个数,分别代表 y1,y2,…,yn。
输出格式
两个数,中间用空格隔开,依次为 V
的个数和 ∧
的个数。
数据范围
对于所有数据,n≤200000,且输出答案不会超过 int64。
y1∼yn是 1 到 n 的一个排列。
输入样例:
5
1 5 3 2 4
输出样例:
3 4
#include <iostream>
#include <cstdio>
#include <cstring>using namespace std;const int N = 2000010;typedef long long LL;int n;
//t[i]表示树状数组i结点覆盖的范围和
int a[N], t[N];
//Lower[i]表示左边比第i个位置小的数的个数
//Greater[i]表示左边比第i个位置大的数的个数
int Lower[N], Greater[N];//返回非负整数x在二进制表示下最低位1及其后面的0构成的数值
int lowbit(int x)
{return x & -x;
}//将序列中第x个数加上k。
void add(int x, int k)
{for(int i = x; i <= n; i += lowbit(i)) t[i] += k;
}
//查询序列前x个数的和
int ask(int x)
{int sum = 0;for(int i = x; i; i -= lowbit(i)) sum += t[i];return sum;
}int main()
{scanf("%d", &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]; //第i个数//在前面已加入树状数组的所有数中统计在区间[1, y - 1]的数字的出现次数Lower[i] = ask(y - 1); //在前面已加入树状数组的所有数中统计在区间[y + 1, n]的数字的出现次数Greater[i] = ask(n) - ask(y);//将y加入树状数组,即数字y出现1次add(y, 1);}//清空树状数组,从右往左统计每个位置右边比第i个数y小的数的个数、以及大的数的个数memset(t, 0, sizeof t);LL resA = 0, resV = 0;//从右往左统计for(int i = n; i >= 1; i--){int y = a[i];resA += (LL)Lower[i] * ask(y - 1);resV += (LL)Greater[i] * (ask(n) - ask(y));//将y加入树状数组,即数字y出现1次add(y, 1);}printf("%lld %lld\n", resV, resA);return 0;
}
2.一个简单的整数问题:
给定长度为 N 的数列 A,然后输入 M 行操作指令。
第一类指令形如 C l r d
,表示把数列中第 l∼r个数都加 d。
第二类指令形如 Q x
,表示询问数列中第 x个数的值。
对于每个询问,输出一个整数表示答案。
输入格式
第一行包含两个整数 N 和 M。
第二行包含 N个整数 A[i]。
接下来 M 行表示 M 条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
1≤N,M≤1e5
|d|≤10000
|A[i]|≤1e9
输入样例:
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<bits/stdc++.h>
using namespace std;
#define lowbit(x) x&-x
#define int long long
const int N=2e5+10;
int n,m;
int a[N],tr[N];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;
}signed main(){cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) add(i,a[i]-a[i-1]);while(m--){char op;cin>>op;if(op=='Q'){int x;cin>>x;cout<<sum(x)<<endl;}else {int l,r;int c;cin>>l>>r>>c;add(l,c),add(r+1,-c);}}return 0;
}
3.一个简单的整数问题2:
给定一个长度为 NN 的数列 AA,以及 MM 条指令,每条指令可能是以下两种之一:
C l r d
,表示把 A[l],A[l+1],…,A[r]都加上 d。Q l r
,表示询问数列中第 l∼r个数的和。
对于每个询问,输出一个整数表示答案。
输入格式
第一行两个整数 N,M。
第二行 N 个整数 A[i]。
接下来 M 行表示 M条指令,每条指令的格式如题目描述所示。
输出格式
对于每个询问,输出一个整数表示答案。
每个答案占一行。
数据范围
1≤N,M≤1e5,
|d|≤10000,
|A[i]|≤1e9
输入样例:
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>using namespace std;typedef long long LL;const int N = 1e5 + 10;
int n, m;
int a[N];
LL tr[N], tri[N];
//tr[]数组是原始数组的差分数组d[i]的树状数组
//tri[]数组是原始数组的差分数组乘以i即i*d[i]的树状数组int lowbit(int x)
{return x & -x;
}
void add(LL c[], int x, int v)
{for (int i = x; i <= n; i += lowbit(i))c[i] += v;
}
LL query(LL c[], int x)
{LL res = 0;for (int i = x; i; i -= lowbit(i))res += c[i];return res;
}
//对应最后一步推导的公式
LL get_sum(int x)
{return query(tr, x) * (x + 1) - query(tri, x);
}
int main()
{scanf("%d%d", &n, &m);//输入数组a[i]for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);//先构造两个数组 d[i] 和 i*d[i]for (int i = 1; i <= n; ++i) tr[i] = a[i] - a[i - 1], tri[i] = tr[i] * i;//原地 O(n) 建树状数组for (int x = 1; x <= n; ++x)for (int i = x - 1; i >= x - lowbit(x) + 1; i -= lowbit(i))tr[x] += tr[i], tri[x] += tri[i];//读入查询while (m--){char op[2];int l, r, c;scanf("%s", op);if (op[0] == 'Q'){scanf("%d%d", &l, &r);printf("%lld\n", get_sum(r) - get_sum(l - 1));}else{scanf("%d%d%d", &l, &r, &c);add(tr, l, c), add(tr, r + 1, -c);add(tri, l, l * c), add(tri, r + 1, (r + 1) * -c);}}return 0;
}
树型结构——树状数组相关推荐
- mysql 转成树_Mysql树型结构2种方式及相互转换
Mysql实现树型结构,数据库上常见有2种方式:领接表.预排序遍历树(MPTT). 领接表方式-- 主要依赖于一个 parent 字段,用于指向上级节点,将相邻的上下级节点连接起来,id 为自动递增自 ...
- 【唠叨两句】如何将一张树型结构的Excel表格中的数据导入到多张数据库表中...
小弟昨天遇到一个相对比较棘手的问题,就像标题说的那样.如何将一张树型结构的Excel表格中的数据导入到多张数据库表中,在现实中实际是七张数据库表,这七张表之间有着有着相对比较复杂的主外键关系,对于我这 ...
- Linux TC 流量控制与排队规则 qdisc 树型结构详解(以HTB和RED为例)
1. 背景 Linux 操作系统中的流量控制器 TC (Traffic Control) 用于Linux内核的流量控制,它规定建立处理数据包的队列,并定义队列中的数据包被发送的方式,从而实现对流量的控 ...
- 树型结构(数据结构)
6.1树的基本概念 树型结构是区别于线性结构的另一大数据结构,它具有分支性和层次性. 树是由n(n>=0)个结点构成的有限集合.n=0的树称为空树:当n!=0时,树中的结点度应该满足下列条件: ...
- dtree和jquery构建树型结构
对于小型的树型应用来说,dtree是一个不错的选择. 先看一眼dtree给的例子 构造静态树 首先引入css文件和js文件 <link rel="StyleSheet" hr ...
- EF架构~单表一对多集合的插入(树型结构)
单表一对多关系很常见,它是一种树形结构,如系统菜单表,部门表,分类表,这些都可以做成单表一对多关系,而这些表做成一对多关系后,如果通过EntityFramework进行插入操作时,会很方便,EF会自动 ...
- C#实现树型结构TreeView节点拖拽的简单功能,附全部源码,供有需要的参考
为什么80%的码农都做不了架构师?>>> 应用软件是否好用就体现在一些细节操作上,开发人员是否考虑到了很多细节,例如一个树形结构的数据若不支持拖拽功能那用起来会很糟糕一些,用户 ...
- go之树型结构深度理解补充
go之树型结构深度理解补充 在上一篇中借用了 Ilija Eftimov 文章来讲解了tree的定义和一些方法.这篇文章主要是讲解在树型结构中如何判断节点与节点之间的关系. A节点是否是B节点的直接上 ...
- Nestable 可移动拖拽的树型结构的使用(jQuery)
利用jQuery可以制作出很好的树型结构.这里介绍一款最近才找到使用的Nestable 可以拖动. 网页中的效果 http://dbushell.github.com/Nestable/ 具体详细介 ...
最新文章
- 使用Pyhthon,OpenCV和ZBar构建移动的条码扫描器
- 数学界的隐士:世界上最奇怪的数学天才,被奖励100万却拒领
- javascript es6 特性简介
- linux内核(4.17.10)配置项详解(x86)
- PHP和ajax详解
- oracle 判断是否位汉字,js判断字符是否是汉字的两种方法小结
- 怎样在电脑上上传图片_电脑上回收站怎样恢复
- [古诗十九首] 西北有高楼 —— 无名氏
- html5的常用标签,HTML5常用标签
- nor flash与nand flash启动的简单比较--APPLE的ARM学习笔记一
- 安装配置NTP服务器
- 王者荣耀钓鱼网源码php,王者荣耀钓鱼页面
- 四级网络工程师试题一
- 计算机网络组建校园网课设报告,计算机网络课设校园网组建.doc
- 利用jink的驱动软件j-flash 合并两个hex的方法,bootloader+app
- 以太网未识别的网络win10_win10以太网为什么无Internet未识别网络?
- Flutter高德定位定位权限管理
- 亚信大数据平台产品经理 杨晋:大数据是怎么应用于技术方面的
- 看似不负责任的菩提祖师,却用另外一种方式,造就了孙悟空的人生
- kestrel虚拟服务器,如何使Kestrel Web服务器监听非本地主机的请求?
热门文章
- icloud连接服务器时出现问题_iCloud服务器连接超时怎么办 试试这个解决方法
- 世界上最权威的管理名言
- HTML innerText
- [Android]视图的控触操作-MotionEvent
- 2021-02-06 SONiC SAI结构 AdapterAdapter Host
- 叫“哥”叫“姐”等于赶客,这样称呼回头率暴增!
- java的继承及其抽象方法
- Linux下ll 命令显示的文件类型
- 什么牌子投影仪好?投影仪买什么牌子的好
- VMware虚拟机Linux 两边出现黑屏幕的妙计解决方法。