slide1 n.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
线段树 PowerPoint Presentation
Download Presentation
线段树

Loading in 2 Seconds...

play fullscreen
1 / 62

线段树 - PowerPoint PPT Presentation


  • 128 Views
  • Uploaded on

线段树. 例题: Picture. 题目大意:墙上贴着一些海报、照片等矩形,所有的边都为垂直或水平。每个矩形可以被其它矩形部分或完全遮盖,所有矩形合并成区域的边界周长称为轮廓周长。要求编写程序计算轮廓周长。 数据量限制: 0≤ 矩形数目 <5000 ; 坐标数值为整数,范围是 [-10000 , 10000] 。. 例题: Picture. 例:下图的三个矩形轮廓周长为 30 :. 例题: Picture. 轮廓的定义: 1 、轮廓由有限条线段组成,线段是矩形边或者矩形边的一部分。 2 、组成矩形边的线段不应被任何矩形遮盖。下面分别是遮盖的两种情况。.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about '线段树' - verity


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
picture
例题:Picture
  • 题目大意:墙上贴着一些海报、照片等矩形,所有的边都为垂直或水平。每个矩形可以被其它矩形部分或完全遮盖,所有矩形合并成区域的边界周长称为轮廓周长。要求编写程序计算轮廓周长。
  • 数据量限制:
  • 0≤矩形数目<5000;
  • 坐标数值为整数,范围是[-10000,10000]。
picture1
例题:Picture
  • 例:下图的三个矩形轮廓周长为30 :
picture2
例题:Picture
  • 轮廓的定义:
  • 1、轮廓由有限条线段组成,线段是矩形边或者矩形边的一部分。
  • 2、组成矩形边的线段不应被任何矩形遮盖。下面分别是遮盖的两种情况。

AB被遮盖

CD被遮盖

picture3
例题:Picture
  • 元线段:
  • 由于坐标都是整数,所以可以把平面看成是一个网格,连接相邻网格顶点的基本线段,称之为“元线段”。
  • 矩形边一定在网格线上,所以矩形的边完全由元线段组成。
picture4
例题:Picture
  • 超元线段:
  • 元线段的数目过多,因此引入超元线段。
  • 根据每个矩形纵向边的横坐标纵向地对平面进行2*N次切割、根据矩形横向边的纵坐标横向地对矩形进行2*N次切割(N为矩形个数)。显然,经过切割后的平面被分成了(2*N+1)^2个区域,如下图所示:

其中像横向边AB、纵向边CD这样的线段就是“超元线段”。超元线段与元线段有着相似的性质,也是组成轮廓的基本单位。所不同的是,超元线段的数目较少,一般为4*N条左右,只和矩形数目有关。

picture5
例题:Picture
  • 离散化:
  • 算法的研究对象是超元线段,但这并不等于逐一枚举,那样耗时过大,而整体考虑又使得问题无从下手。有一种考虑方法是折中的,即既不研究每一条超元线段,也不同时研究所有的超元线段,而是再进一步优化问题的离散化,即将超元线段分组研究。夹在两条纵向分割边的超元线段自然地分为一组,它们的共同点是长度相同,并且端点的横坐标相同。纵向线段也可以进行类似的离散化。
picture6
例题:Picture
  • 这样的离散化处理后,使得问题规模降低,以此为基础,算法的框架可以基本确定为:
  • 对平面进行分割。
  • 累加器ans  0。
  • 研究每组超元线段,检测其中属于轮廓的部分的长度,并把这一长度累加入ans。
  • 输出ans的值。
picture7
例题:Picture
  • 映射结构的建立(对平面进行分割):
  • 算法的基础是问题的离散化,要进行平面“分割”,一般需要记录分割点,通常采用映射来记录分割点。直观的做法是采用一维数组形式,下标表示分割点的编号,数组元素表示分割点的坐标。利用下标与数组元素的自然对应,实现映射。应该说,这样表示是比较自然的,实现也比较方便。数组的优点主要是存取方便,且可以在O(NlogN)时间内排序。
picture8
例题:Picture
  • 确定每组超元线段中有多少条属于轮廓:
  • 累计扫描的方法:以一组水平方向的超元线段为例,把一组超元线段按照从上到下的顺序排序。如果第i条超元线段属于某个矩形的上边界,那么令数组元素a[i]=1;如果是下边界,那么令数组元素a[i]=-1。同时,设立累加器add,从上至下扫描超元线段,一条超元线段I属于轮廓的情况有两种:
  • A[I]≠0且扫描到该超元线段未累加时add = 0;
  • A[I]≠0且扫描到该超元线段累加之后add = 0。
picture9
例题:Picture
  • 使用线段树:
  • 线段树是描述单个或若干区间并的树形结构,属于平衡树的一种。使用线段树要求知道所描述的区间端点可能取到的值。换句话说,设A[1..N]是从小到大排列的区间端点集合,对于任意一个待描述的闭区间P=[x,y],存在1≤i≤j≤N使得x=a[i]且y=a[j],这里i, j称为x,y的编号。可以看到,即使是实数坐标,在线段树中也只有整数含义。以下所说的区间[x, y]如无特殊说明,x、y均是整数,即原始区间顶点坐标的编号。
picture10
例题:Picture
  • 线段树是一棵二叉树,将数轴划分成一系列的初等区间[i, i+1] 。每个初等区间对应于线段树的一个叶结点。
picture11
例题:Picture
  • 线段[3, 6]在线段树中的表示:
picture12
例题:Picture

线段树节点的数据域:

  • Count:区间被完全覆盖的次数;
  • 测度M:区间被线段覆盖的长度;
  • 连续段数:区间可以被分为多少个连续的区间。
picture13
例题:Picture

节点[i, j]的测度:

  • 如果count[i][j] > 0:M = a[i] – a[j];
  • 如果count[i][j] = 0:
    • 如果是叶节点:M = 0
    • 如果不是叶节点:M值为左右子树的M值之和。
picture14
例题:Picture

节点[i, j]的连续段数:

  • 需要另外两个Bool变量lbd和rbd,分别表示节点所表示区间的左右端点是否被覆盖。
  • 该节点count>0,那么lbd = true
  • 该节点count=0
    • 该节点是叶节点,那么lbd = fauth
    • 该节点不是叶节点,那么lbd = 左子节点的lbd
picture15
例题:Picture

节点[i, j]的连续段数l:

  • 如果该节点Count > 0,那么l = 1;
  • 如果该节点Count = 0:
    • 如果左子树的rbd and 右子树的lbd为真:那么该节点的l=左子树的l+右子树的l-1
    • 否则,该节点的l=左子树的l+右子树的l
picture16
例题:Picture
  • 如图所示:超元线段CD与EF被矩形AGHB遮盖,不属于轮廓;而与之相邻DD’与FF’则“摆脱”了矩形的遮盖,属于轮廓的一部分。可以这样分析:从左往右,CD、EF首先被遮盖,但随着BF的出现,对DD’、FF’的遮盖自然消失。这一点,正是相邻超元线段组的内在联系。用线性结构无法表示出这一联系,因为各组的累计扫描过程是独立的。现在我们用树形结构来表示将较好地解决这一问题,因为线段树支持插入与删除及动态维护,可以有机联系各组超元线段的状态。
picture17
例题:Picture
  • 我们把“从左往右”当作一个扫描的过程,若将其严格地描述,可以得到一个称为线段扫描的过程:
  • 设立扫描线段L。
  • L从左往右扫描,停留在每一超元线段组上。
  • L的状态用线段树来表示,每一条纵向的矩形边看作一个待合并区间。线段树的连续段数*2表示该组超元线段属于轮廓的线段数目。
  • 扫描过程中动态地维护L的状态。
picture18
例题:Picture
  • 维护线段树的状态:
  • 线段树L初始化为空,即线段树刚建好时的情形。
  • 扫描时,遇到矩形左边,将其插入(Insert)线段树。
  • 扫描时,遇到矩形右边,将其从线段树中删除(Delete)。由于从左往右扫描,事先插入了该矩形的左边,所以删除合法。
slide21
例一:苹果树
  • 题目大意:有一棵包含n个节点的树,这些节点用1到n这n个整数编号,其中根节点的编号一定是1。在最初时刻,每个节点都有一个苹果;此后,会进行两种操作:
  • C(i):如果第i个节点此时有苹果,那么把苹果拿走;如果这个节点此时没有苹果,则在这个节点放上一个苹果。
  • Q(i):问此时以第i个节点为根的子树上共有多少个苹果。要求对每一个Q(i)操作输出正确的结果。
slide22
例一:苹果树
  • 输入:输入数据的第一行包含一个整数n(n ≤100000),表示树所包含的节点数;接下来有n-1行,每行包含两个整数v和u,表示节点v和节点u之间有一条边。然后是一个整数m(m≤100000),表示操作的个数。接下来有m行,每一行描述一个操作。
  • 输出:输出每一个Q(i)操作的正确的结果。
slide23
例一:线段树
  • 样例输入: 样例输出:
  • 3 3
  • 1 2 2
  • 1 3
  • 3
  • Q 1
  • C 2
  • Q 1
slide24
例一:线段树
  • 样例输入所对应的树为:
  • 1(1)
  • 2(1) 3(1)
  • 样例输入的每一步操作对树的影响:
  • Q1:(输出:3) C2(无输出) Q1(输出:2)
  • 1(1) 1(1) 1(1)
  • 2(1) 3(1) 2(0) 3(1) 2(0) 3(1)
slide25
例一:苹果树
  • 分析:最容易想到的方法是模拟。首先按照题目描述建树,每一个节点保存以这个节点为根的子树中的苹果数目,此时这个节点是否有苹果,以及该节点的父节点。这样,对于每一个Q(i)操作,可以在O(1)的时间内直接得到结果;而对于C(i)操作,由于要修改节点i以及它的所有祖先,所以这个操作的复杂度与节点i的深度有关。在最坏情况下,每一次修改的复杂度可能都是O(n)的,因此模拟算法的复杂度大约为O(mn),是不可行的。
slide28
例一:苹果树
  • 分析:怎样通过略微提高Q(i)操作的复杂度来降低进行C(i)操作时的复杂度?
slide29
例一:苹果树
  • 深度优先搜索(DFS)的括号定理:
  • 在对一个(有向或无向)图G=(V, E)的任何深度优先搜索中,对于图中的任意两个定点u和v,下述三个条件只有一个成立:
  • 区间[d[u], f[u]]和区间[d[v], f[v]]是完全不相交的,且在深度优先森林中,u或v都不是对方的后裔。
  • 区间[d[u], f[u]]完全包含于区间[d[v], f[v]]中,且在深度优先树中,u是v的后裔。
  • 区间[d[v], f[v]]完全包含与区间[d[u], f[u]]中,且在深度优先树中,v是u的后裔。
slide30
例一:苹果树
  • 根据括号定理,可以发现,如果把树中的节点按照深度优先搜索发现的顺序排列,那么任意一棵子树上的所有节点在这个序列中必然是连续的。
slide31
例一:苹果树

1

按照DFS的发现顺序对节点排序:

1 2 4 7 8 3 5 6 9 10

2

3

6

5

4

8

10

7

9

slide32
例一:苹果树

1

按照DFS的发现顺序对节点排序:

1 2 4 7 8 3 5 6 9 10

2

3

6

5

4

8

10

7

9

slide33
例一:苹果树

1

按照DFS的发现顺序对节点排序:

1 2 4 7 8 3 5 6 9 10

2

3

6

5

4

8

10

7

9

slide34
例一:苹果树
  • 分析:如果记录下每棵子树所对应的区间,然后用线段树维护区间内苹果数的总和。就可以在O(lgn)的时间复杂度内完成Q(i)和C(i)操作。
  • 线段树的使用:线段树的每个节点对应了按DFS顺序排序后的一段区间,每个节点设一个变量表示该区间数字之和。
slide35
例一:苹果树
  • 由DFS访问序列1 2 4 7 8 3 5 6 9 10建立的线段树:

R:[1, 10] S:10

R:[1, 5] S:5

R:[6, 10] S:5

R:[1, 3] S:3

R:[4, 5] S:2

R:[6, 8] S:3

R:[9, 10] S:2

R:[1, 2] S:2

R:[3, 3] S:1

R:[4, 4] S:1

R:[5, 5] S:1

R:[6, 7] S:2

R:[8, 8] S:1

R:[9, 9] S:1

R:[9, 10] S:1

R:[1, 1] S:1

R:[2, 2] S:1

R:[6, 6] S:1

R:[7, 7] S:1

slide36
例一:苹果树
  • 由DFS访问序列1 2 4 7 8 3 5 6 9 10建立的线段树:

R:[1, 10] S:9

R:[1, 5] S:4

R:[6, 10] S:5

R:[1, 3] S:3

R:[4, 5] S:1

R:[6, 8] S:3

R:[9, 10] S:2

R:[1, 2] S:2

R:[3, 3] S:1

R:[4, 4] S:0

R:[5, 5] S:1

R:[6, 7] S:2

R:[8, 8] S:1

R:[9, 9] S:1

R:[9, 10] S:1

R:[1, 1] S:1

R:[2, 2] S:1

R:[6, 6] S:1

R:[7, 7] S:1

slide37
例一:苹果树
  • 线段树中Sum值的维护:线段树的叶节点表示的是苹果树中一个节点出的苹果个数,这个值是可以直接得到的;而线段树的内部节点的Sum值等于它的两个子节点Sum值之和。
harmony forever
例二:Harmony Forever
  • 题目大意:给定一个集合S(初始为空),以及两种操作:
  • B(x):如果正整数x目前不在集合S中,把x加入集合S。
  • A(y):输出目前集合S中除以正整数y所得余数最小的元素是第几个被加入集合的。如果有多个这样的元素,输出最后被加入集合的那个。如果没有,输出-1。
  • 每组输入数据的时间限制为5秒。
harmony forever1
例二:Harmony Forever
  • 输入:第一行包含一个正整数t(t≤40000),表示要进行操作的数目。接下来有t行,来描述依次出现的每一个操作,这些操作的格式为"B X"或"A Y",其中X, Y是正整数并且满足:1≤X, Y≤500000
  • 输出:对于每一个"A Y"命令,输出一个整数,是集合中模y结果最小的元素是第几个被加入集合的。如果有多个满足条件的元素,输出最后加入集合的那个,如果没有这样的元素,输出-1。
harmony forever2
例二:Harmony Forever
  • 样例输入: 样例输出:
  • 5 1
  • B 1 2
  • A 5 1
  • B 10
  • A 5
  • A 40
harmony forever3
例二:Harmony Forever

初始状态:

B 1:

B 10:

A 5: 此时集合中10≡0(mod 5)最小,而 10是集合中的第二个元素,所以输 出2。

harmony forever4
例二:Harmony Forever
  • 最简单的方法仍然是模拟的方法:用一个栈来表示集合。遇到添加元素的操作时,就把要添加的元素插入栈中;遇到查询操作时,扫描整个栈来找到需要的结果。采用这种方法,每次插入操作的时间复杂度为O(1),而查询操作的复杂度是O(n),要对栈中的每个元素取模,这里的n是进行这步查询操作时栈中元素的个数。
harmony forever5
例二:Harmony Forever
  • 例:在集合{5, 2, 13, 6, 15, 19}中找模8最小的元素。
  • 先把集合中的元素划分为三类:
  • 第一类中的元素满足0≤x < 8:{2, 6, 5};
  • 第二类中的元素满足8≤x < 16:{13, 15};
  • 第三类中的元素满足16≤x < 24:{19}。
  • 然后找出每一类中的最小元素:2,13,19
  • 最后在这些最小元素中寻找模8所得结果最小的,就是最终结果:2≡2(mod 8),13≡5(mod 8),19≡3(mod 8)。所以集合中模8最小的元素是2。
harmony forever6
例二:Harmony Forever
  • 可以使用线段树查找指定区间内并且已经在集合中的最小元素:
  • 令线段树的每个节点表示一个整数区间,另外用一个变量表示这个区间中已经被插入集合的最小元素。
harmony forever7
例二:Harmony Forever
  • 由1-10这十个整数构成的线段树,并且假设集合为:{1, 3, 7}

R:[1, 10] Min:1

R:[1, 5] Min:1

R:[6, 10] Min:7

R:[1, 3] Min:1

R:[4, 5] Min:∞

R:[6, 8] Min:7

R:[9, 10] Min: ∞

R:[1, 2] Min:1

R:[3, 3] Min: 3

R:[4, 4] Min: ∞

R:[5, 5] Min: ∞

R:[6, 7] Min:7

R:[8, 8] Min: ∞

R:[9, 9] Min: ∞

R:[9, 10] Min: ∞

R:[1, 1] Min:1

R:[2, 2] Min:2

R:[6, 6] Min: ∞

R:[7, 7] Min:7

harmony forever8
例二:Harmony Forever
  • 线段树中Min值的维护:叶子节点的Min值可以由这个整数是否在集合中直接确定,如果在那么Min值就是这个叶子节点表示的那个整数,如果不在,Min值就是正无穷;而内部节点的B值可以由两个叶子节点的B值比较大小运算得到。
  • 在查找指定区间的最小值时,只有左子树上Min值是正无穷或者与指定区间没有交集时,才需要进入右子树。
harmony forever9
例二:Harmony Forever
  • 假设集合为{1, 2, 7},并且给定的区间为[2,9],在下面这棵线段树中查找最小值时所经过的节点为:

R:[1, 10] Min:1

R:[1, 5] Min:1

R:[6, 10] Min:7

R:[1, 3] Min:1

R:[4, 5] Min:∞

R:[6, 8] Min:7

R:[9, 10] Min: ∞

R:[1, 2] Min:1

R:[3, 3] Min: ∞

R:[4, 4] Min: ∞

R:[5, 5] Min: ∞

R:[6, 7] Min:7

R:[8, 8] Min: ∞

R:[9, 9] Min: ∞

R:[9, 10] Min: ∞

R:[1, 1] Min:1

R:[2, 2] Min:2

R:[6, 6] Min: ∞

R:[7, 7] Min:7

harmony forever10
例二:Harmony Forever
  • 由于给出了输入数据的范围[1, 500000],所以需要建立一棵根节点表示区间为[1,500000]的线段树。每加入一个新元素的时间复杂度为O(lg500000);而每一次查找操作与B Y中的Y有关,一次查找需要在线段树中搜索(500000/Y)次,每次搜索的复杂度是O(lg500000),所以总的复杂度大约为:O(lg500000) * 500000/Y。容易发现Y越大,这种方法复杂度越低。当Y很小时,复杂度比较高。
harmony forever11
例二:Harmony Forever
  • 于是,将问题分两种情况解决:当Y比较小时,采用直接的模拟方法。当Y比较大时,由于取模操作的开销比较大,采用线段树的方法解决。
k th number
例三:K-th Number
  • 题目大意:给定n个正整数组成的集合A[1…N],要求设计一个在线算法,对于询问Q(i, j, k)返回A[i…j]的第k大元素。数组中的元素不超过100000个,元素大小不超过1000000000,询问次数不超过5000,每组输入数据的时间限制为2秒。
k th number1
例三:K-th Number
  • 分析:假设是在整个数组中找第k大元素,并且令函数S(x)表示数组中小于x的元素的个数,令函数L(x)表示数组中大于x的元素的个数。则第k大元素xk满足以下关系:
  • S(xk+t) ≥k (t > 0)
  • S(xk –t) <k (t ≤ 0)
k th number2
例三:K-th Number
  • 根据这两条性质,就可以采用二分搜索的方法确定数组的第k大元素:
  • for (bound = 0, range = (1 << 30); range; range /= 2) {
  • if (S(bound + range) < k) bound += range;
  • }
  • 最终的到的bound就是数组中的最小元素。这里省略了S(x)的计算过程。
k th number3
例三:K-th Number
  • 例:求数列3,9,6,1,6,8中的第4大元素。
  • 求解过程:在1-9的整数中做二分查找,初始化bound=0,range=16,k=4,以下蓝色的数字表示bound的当前值,红色的数字表示bound+range的值。
  • 0 1 2 3 4 5 6 7 8 9 S(16)=6; S(16) < k为假
  • 0 1 2 3 4 5 6 7 8 9 S(8) = 4; S(8) < k为假
  • 0 1 2 3 4 5 6 7 8 9 S(4) = 2; S(4)< k为真
  • 0 1 2 3 4 5 6 7 8 9 S(6) = 3; S(6) < k为真
  • 0 1 2 3 4 5 67 8 9 S(7) = 4; S(7) < k为假
  • 0 1 2 3 4 5 6 7 8 9
  • 所以:最终结果为6
k th number4
例三:K-th Number
  • S(x)的计算方法:如果给定的数列是排好序的,那么同样可以用二分查找的方法来求S(x)。所以对于任意的序列只要先排序,此后对于任意的x,计算S(x)的时间复杂度都是O(lgn)。
k th number5
例三:K-th Number
  • 回到题目
  • 题目要求能够求出任意给定区间内的第k大元素,事实上只要我们能够以比较低的时间复杂度求出任意区间内的S(x)就可以解决原来的问题了。
k th number6
例三:K-th Number
  • 如果把给定的区间划分成若干比较小的区间,而每个比较小的区间又都在预处理中排好序,那么只要求每个小区间的S(x)值,然后累加起来就能得到给定区间的S(x)值。
  • 可以考虑用线段树把任意给定的区间划分成若干个区间。
k th number7
例三:K-th Number
  • 线段树的使用:
  • 线段树的每个节点代表的数列的一个区间,每个节点都保存其对应区间所属元素从小到大排好序的序列。
k th number8
例三:K-th Number
  • 序列6,9,3,1,8,6产生的线段树:

6,9,3,1,8,6

1,3,6,6,8,9

6,9,3

3,6,9

1,8,6

1,6,8

6,9

6,9

3

3

1,8

1,8

6

6

6

6

9

9

1

1

8

8

k th number9
例三:K-th Number
  • 线段树的保存:
  • 因为n至多为100000,所以线段树的高度不会超过20层,每一层都包含了这n个元素,并且仅包含这n个元素,于是可以使用一个20×100000的数组来保存排好序的序列。每一行用来保存线段树一层的节点的排序信息,而每个节点中需要保存它自己所对应区间的排序序列在二维数组中的位置。
k th number10
例三:K-th Number
  • 例:数列6,9,3,1,8,6的存储:
k th number11
例三:K-th Number
  • 线段树的构造:
  • 可以看出,线段树的节点中所保存的信息恰好构成了归并排序的中间过程。因此可以采用归并排序的框架来构造线段树。
k th number12
例三:K-th Number
  • 总结:
  • 总体思路:用函数S(x)做判定,通过二分搜索查找答案。
  • 通过线段树保存特定区间的排序结果,利用二分搜索容易计算这些区间内的S(x)的值,然后将指定的区间分割为这些特定区间的并,通过对分割成的这些区间求和来的到指定区间的S(x)的值。