树状数组及其应用双语版

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

• 对于询问(x1,y1)-(x2,y2),
ans=getsum(x2,y2)-getsum(x2,y1-1)-getsum(x11,y2) +getsum(x1-1,x2-1);
树状数组下标必须从1开始
Superbrother 神牛
• 到此,我们已经学习完树状数组的基本内 容。 • 树状数组的应用非常广泛,变形极多,灵 活性强,很多题目经过一系列转化后可以 使用树状数组解决。 • 下面,我将通过其他几个例题介绍如何通 过有效的转化使用树状数组解题。
样例输出 100
• 此题条件简单,但并不直观 • 将区间坐标化,我们发现,对于每头牛, 要求的就是其左上方的牛的个数。 • 同stars,注意判断点重合的情况
4、逆序对
• 题目大意 • 给定一个序列a[1]..a[n],对于任意i,j,如果i<j 并且a[i]>a[j],我们说这是一个逆序对。 • 你的任务是输出逆序对的个数 • N<=100000 a[i]<=maxlongint
Function getsum(x,y):integer;(求出矩阵(1,1)~(x,y)点值 和) Var z,t:longint; Begin t:=0; while x>0 do begin z:=y; while z>0 do begin t:=t+c[x,z]; z:=z-lowbit(z); end; x:=x-lowbit(x); end; getsum:=t; End;
• 此题是树状数组的经典应用 • 首先离散化坐标使数据范围减小,为使用 树状数组创造了条件 • 按横坐标排序,使得原题中“左下方”两 个条件限制转化为“下方”这一单一限制 • 可以轻松运用树状数组解决
现在,我们将树状数组推广到二维
先看一道神奇的题目
2、Superbrother打鼹鼠(vijos 1512)
•由此我们可以得出修改与查询操作
Procedure add(k,delt); Begin while k<=limit do begin C[k]:=C[k]+delta; k:=k+lowbit(k); End; End; void add(k,delt) { while(k<=limit) { c[k]+=delt; k+=lowbit(k); } }
•当要查询a[i..j]累加和时,可以先求出a[1..j],a[1..i-1],然后 相减
• Function getsum(k:integer):integer; Var t:integer; Begin t:=0; while k>0 do begin t:=t+c[k]; k:=k-lowbit(k); end; getsum:=t; End;
• 题目大意 • 给定一个n*n的正方形区域,左上角为(0,0) , 右下角为(n-1,n-1),初始所有整点权值为0。 任意时刻有两种操作: • 1、在一个整点处权值加K • 2、询问一个矩形内整点权值和 • N<=1024
• 这道题目是神牛Superbrother原创,很难! • 此题可以用二维线段树或二维树状数组解 决。 • 二维树状数组的代码与一维及其相似。
• 如果直接用数组模拟,那么修改是O(1),查 询是O(n),如果维护另一个数组b[i],表示 a[1]到a[i]的和,那么修改是O(n),查询时 O(1)。 • 一个程序的时间复杂度取决于其中最大的 时间复杂度。 • 树状数组的目的就是平摊修改和查询的时 间,使复杂度由O(n)降到O(lgn)。 • 树状数组是一种数据结构,这种结构能让 我们的程序变得高效,数据结构大多数时 候是为了优化算法而用的。
i
1 2 3 4 5
3 0 0 1 0 1
• 例如n=5,输入100 6 7 2 3 • 首先离散化,输入变为5 3 4 1 2 • 顺序扫描
i
1 2 3 4 5
4 0 0 1 1 1
• 例如n=5,输入100 6 7 2 3 • 首先离散化,输入变为5 3 4 1 2 • 顺序扫描
i
1 2 3 4 5
• 枚举? • 代码简单,时间复杂度达到O(n2) • 有没有代码简单、时间复杂度低的方法?
• 此题有效的算法很多,树状数组可以简洁 快速的解决此问题。 • 如何构建树状数组? • 如何处理巨大的(x,y)? • 如何处理“左下方”?
• 首先,离散化y坐标,然后按x坐标从小到大 排序,x相同则y坐标由小到大,从左到右扫 描每个点,这样可以保证已经插入树状数组 的点都在左侧或正下侧。 • 我们只需寻找有 多少点位于当前点下 方,很容易想到树状 数组。处理完当前点 后,将其按y坐标插入 树状数组,即让a[y]加1
• 与线段树和其他数据结构相比,树状数组 代码简单,常数小,在其能解决范围内或 经过转化使用树状数组,可以极大的降低 编程复杂度,使代码清晰,简洁
优秀的数据结构!
例题、Stars(ural 1028)
• 题目大意 • 平面中有N个点,对于 每个点(x,y),要求输 出在其左下方(包括正 左正下)点的个数。 • N<=100000; • x,y<=maxlongint
样例输入 2 10 C2122 Q22 C2121 Q11 C1121 C1212 C1122 Q11 C112• 此题是区间求反问题,而树状数组所维护 的是区间和问题。 • 如何转化? • 注意到所求为一个点 的值,而点值只与 求反操作次数有关
• 首先考虑一维情况 • 原问题变为:对于一个数列,每次对一段 区间取反,询问一个点的值。 • 由于一个点的值只与取反次数有关,所以 我们记录每个点的取反次数。 • 树状数组支持的操作是修改一个点的值, 查询一段区间的和。而此题恰恰相反。 • 如何改变树状数组的意义? •
5 1 0 1 1 1
小结
• 以上几道例题比较简单,属于树状数组的 基础用法,主要起到数据结构的作用,用 来对数据进行快速的存储和处理。 • 树状数组与其他数据结构相比,最大的特 点就是代码简单,常数小,从而得到广泛 的应用。 • 树状数组的基本操作就是两个,修改某个 元素的值,求区间累加和。
括号
• Superbrother经过研究,制定了一套详细的 计划。计划可以描述为若干个操作,每个 操作在[l,r]中所有点种一棵颜色特殊的树。 • 教导处随时都会检查,方式为给定一个点, 要求他回答这个点一共种过多少种不同的 树。 • 输入第一行为n,m 以下m行,可能是一次种 树,也可能是一次询问。
lowbit(i)=i and (-i) 或i and (i xor (i-1))
结论
• 1、节点i的父亲节点为i+lowbit(i) • 2、节点i的最近不相交前驱为i-lowbit(i) • 3、若需改变a[i],则c[i]、c[i+lowbit(i)]、 c[i+lowbit(i)+lowbit(i+lowbit(i)]……一直加 到上限,就是需要改变的c数组中的元素。 • 4、若需查询s[i],则c[i]、c[i-lowbit(i)]、c[ilowbit(i)-lowbit(i-lowbit(i))]……就是需要累 加的c数组中的元素。 • 5、以上的修改和查询都是log2(n)级别的。
• 括号表示法是运用树状数组解题的重要方 法之一。 • 应用括号表示,可以将一部分修改区间、 查询点值的题目转化为修改点值、查询区 间,从而可以使用树状数组。
5、Superbrother种树
• 实验中学东校坐落在济南市郭店镇。为了 迎全运,树新风,需要在一条长为n的道路 上种若干种树。道路表示为[0,n],共n+1个 点。由于经费紧张,教导处将这个重任交 给了信息组头号神牛Superbrother,希望他 合理分配树木,使道路更加漂亮。
• 此题直接枚举同样需要n2的时间 • 此题同样解法较多,可以使用分治法类似 归并排序,也可以使用树状数组。 • 此题和stars同样有两个限制: “i<j并且 a[i]>a[j]” • 应用同一思路,顺序扫描,将其转化为一 个限制——a[i]>a[j]。
• 首先对a数组离散化 • 按顺序扫描,只需找到有多少比a[i]大的数 已经出现过。这可以用树状数组维护。 • 初始时,数组全为0。每次扫描到a[i],用树 状数组求出a[i]+1~max中出现过多少个数, 然后将a[i]插入树状数组。
树状数组及其应用
Binary Indexed Tree
• 引例
【题目描述】 输入一个数列A1,A2….An(1<=N<=100000),在数列上进行 M(1<=M<=100000)次操作,操作有以下两种: 格式为C I X,其中C为字符“C”,I和 X(1<=I<=N,|X|<=10000)都是整数,表示把把a[I]改为X 格式为Q L R,其中Q为字符“Q”,L和R表示询问区间为 [L,R](1<=L<=R<=N),表示询问A[L]+…+A[R]的值。 【输入】 第一行输入N(1<=N<=100000),表述数列的长度,接下来N 行,每行一个整数(绝对值不超过10000)依次输入每个数; 接下来输入一个整数M(1<=M<=100000),表示操作数量, 接下来M行,每行为C I X或者Q L R。 【输出】 对于每个Q L R 的操作输出答案。
• 此题与前面几题不同,要求修改一段区间 的值,询问一个点的值。 • 使用线段树可以轻松处理。 • 有没有更简单、快速的方法?
• 利用括号,转化为树状数组 < >
• 要将修改区间操作转化为修改点的操作, 只有在区间端点做文章。 • 每次修改区间,便在区间两端加括号。这 样,每次询问时只需要输出从0~这个点中 左括号数-右括号数。
3、Cows(pku 2481)
• 题目大意 • 有n头牛,每头牛喜欢吃[s[i],e[i]]这个区间的 草。如果对于i、j两头牛,s[i]<=s[j]且 e[j]<=s[i]且等号最多成立一个,那么i比j强。 输出每头牛比多少头牛强。 • N,s,e<=10^5
• 样例输入 3 12 03 34
int getsum( int k) {
int t=0;
while (k>0) {
t+=c[k];
k- =lowbit[k]; } return t; }
• 在实际应用中,通常不需要保存原数组a, 只保存数组c即可。因此,树状数组几乎不 需要附加空间。 • 通过上面的学习可以看出:树状数组所能 支持的操作是修改点值、查询区间和。
• 树状数组对于原数组a维护另一个数组c,c[i]表示 sum(a[j]),i-lowbit(i)+1<=j<=i,其中lowbit(i)表示取出i二进制 表示中最右边的1。 • 例如lowbit(6)=lowbit(110)2=(10)2=2 • 所以 c[6]=a[5]+a[6]; • lowbit(4)=lowbit(100)2=(100)2=4 • c[4]=a[1]+a[2]+a[3]+a[4] • lowbit[5]=lowbit(101) 2 =(1) 2=1; • c[5]=a[5] • 其比较简单的算法为 lowbit(i)=i & (-i) 或i & (i ^ (i-1))
• 例如n=5,输入100 6 7 2 3 • 首先离散化,输入变为5 3 4 1 2 • 顺序扫描
i
1 2 3 4 5
1 0 0 0 0 0
• 例如n=5,输入100 6 7 2 3 • 首先离散化,输入变为5 3 4 1 2 • 顺序扫描
i
1 2 3 4 5
2 0 0 0 0 1
• 例如n=5,输入100 6 7 2 3 • 首先离散化,输入变为5 3 4 1 2 • 顺序扫描

< >< > > < >
• 具体的,对于每个修改操作[l,r],将树状数 组中c[l]+1,c[r+1]-1。对于询问操作t,输出 getsum(t)。 • 时间复杂度O(mlgn)

< >< > > < >
6、Matrix(pku 2155)
• 题目大意 • 给定一个n*n的01矩阵,初始全部为0。要 求维护两个操作: • 1、C x1 y1 x2 y2 ,子矩阵(x1,y1)~(x2,y2) 中数 值全部取反 • 2、Q x y ,询问点(x,y)的值
相关文档
最新文档