线段树

线段树之入门篇

断了今生、忘了曾经 提交于 2020-01-26 15:40:07
线段树 (interval tree) 是把区间逐次二分得到的一树状结构。它反映了包含归并排序在内的非常多分治算法的问题求解方式。 上图是一棵典型的线段树。它对区间[1,10]进行切割。直到单个点。这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分。记 L = b - a 2. 一共同拥有log2L层 3. 给定一个点p。从根到叶子p上的全部区间都包括点p。且其它区间都不包括点p。 4. 给定一个区间[l; r],能够把它分解为不超过2log2 L条不相交线段的并。 当中第四点并非非常显然。图8.1显示了[2, 8]的分解方式,深灰色结点为分解后的 区间,浅灰色结点是递归分解过程中经过的结点。 为了叙述方便,以下称树中的结点 所相应的区间为树中区间。   从第3点和第4点能够得出结论:改动一个点仅仅用改动log2 L个树中区间信息。而统计一个区间仅仅须要累加2log2 L个树中区间的信息,且訪问的总结点数是O(log L)的。 两个操作都非常easy实现。    动态统计问题I 有一个包括n个元素的整数数组A,每次能够改动一个元素,也能够询问某一个区间[l; r]内全部元素的和。怎样设计算法,使得改动和询问操作的时间复杂度尽量低?   方法一 直接做, 则改动操作是O(1)的, 但询问须要进行累加, 时间复杂度为O(r ¡ l),最坏情况为O(n)。   方法二

线段树入门

♀尐吖头ヾ 提交于 2020-01-26 15:39:07
 // 转载自: http://www.cnblogs.com/superbin/archive/2010/08/02/1790467.html 感觉讲的很详细。也很实用。果断转~感谢楼主~~~ 线段树入门 线段树 (interval tree) 是把区间逐次二分得到的一树状结构,它反映了包括归并排序在内的很多分治算法的问题求解方式。 上图是一棵典型的线段树,它对区间[1,10]进行分割,直到单个点。这棵树的特点 是: 1. 每一层都是区间[a, b]的一个划分,记 L = b - a 2. 一共有log2L层 3. 给定一个点p,从根到叶子p上的所有区间都包含点p,且其他区间都不包含点p。 4. 给定一个区间[l; r],可以把它分解为不超过2log2 L条不相交线段的并。 其中第四点并不是很显然,图8.1显示了[2, 8]的分解方式,深灰色结点为分解后的 区间,浅灰色结点是递归分解过程中经过的结点。为了叙述方便,下面称树中的结点 所对应的区间为树中区间。   从第3点和第4点可以得出结论:修改一个点只用修改log2 L个树中区间信息,而统计一个区间只需要累加2log2 L个树中区间的信息,且访问的总结点数是O(log L)的。两个操作都很容易实现。    动态统计问题I 有一个包含n个元素的整数数组A,每次可以修改一个元素,也可以询问某一个区间[l; r]内所有元素的和

P6012 【模板】线段树分裂

我与影子孤独终老i 提交于 2020-01-26 09:42:17
(因为没有认证,所以这道题就由 Froggy 上传) 线段树分裂用到的地方确实并不多,luogu上以前也没有这道模板题,所以就出了一道,实在是想不出怎么出模板了,所以这道题可能可以用一下其他的算法水过去. 前置芝士 线段树 : 本题中用到的是权值线段树(查询每个数在序列中出现的次数,序列中第k大的数等操作). 线段树合并: 为了增加一下码量才放上的 . 线段树分裂 既然是模板题,这个自然才是重点. 以下这样一颗线段树: 需要将橙色线段覆盖的部分分裂出来,需要建一颗新的树,当一个节点所代表的区间与需要分裂的区间有交时需要开一个新的节点(绿色部分),原来的树中需要将那些整个被分裂的部分直接接到新的树下面,并且将于原来的树的边断开(红色部分),可以发现被断开的边最多只会有 \(\log_2N\) 条,所以最终每次分裂的时间复杂度就是 \(\log_2N\) (证明方法与区间修改为 \(N\log_2N\) 相同). 代码 #include<bits/stdc++.h> #define rap(i,first,last) for(int i=first;i<=last;++i) //线段树标准define #define Lson (tree[now].lson) #define Rson (tree[now].rson) #define Middle ((left+right)>>1)

【POJ2750】最大连续和 线段树

喜欢而已 提交于 2020-01-26 01:45:08
题目描述   给出一个含有N个结点的环,编号分别为1..N,环上的点带有权值(可正可负),现要动态的修改某个点的权值,求每次修改后环上的最大连续和,但不能是整个序列的和。 题目大意 单点修改,求环上的最大和。 数据范围 (4<=N,M<=100000)(不会爆int) 样例输入 5 3 -2 1 2 -5 4 2 -2 5 -5 2 -4 5 -1 样例输出 4 4 3 5 解题思路   线段树,维护:↓(最大值与最小值是指这一段的和的最大值与最小值) Max(区间最大值),MaxL(从左端开始的区间最大值),Maxr(从右端开始的区间最大值) Min(区间最小值),MinL(从左端开始的区间最小值),Minr(从右端开始的区间最小值) Sum(区间和),MinMin(这个东西我也不确定是否为必须的,以前做没有修改的环上的最大和时用过的,就加上吧233)   所以对环上最大和的询问的Ans就是(1-n这个区间的最大值)和(Sum-区间最小值)中的最大值A.A 代码 #include <bits/stdc++.h> using namespace std; inline int Getint(){int x=0,f=1;char ch=getchar();while('0'>ch||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while('0'<=ch

线段树

假如想象 提交于 2020-01-25 19:50:05
/* Please write complete compilable code. Read input from standard input (STDIN) and print output to standard output(STDOUT). For more details, please check http://www.interviewstreet.com/recruit/challenges/faq/view#stdio */ #include <iostream> #include <cstring> #include <algorithm> #include <vector> #include <cmath> #include <string> #include <map> #include <set> #include <fstream> using namespace std; typedef long long ll; const int MAXN = 100010; const int QUADRANT = 4; int ans[QUADRANT]; inline int get_quadrant(int x, int y){ if(x > 0){ if(y > 0)return 0; return 3; } if(y > 0)return 1;

对主席树的理解以及使用

為{幸葍}努か 提交于 2020-01-24 18:36:47
引入 一个长度为 \(n\) 的数组,有 \(m\) 次查询,每次查询区间 \([l,r]\) 内第 \(k\) 小的元素。 如果使用暴力,肯定不可以 使用线段树?可是我只会查询区间最值啊。 那么我们把问题再次简化一下,查询 \([1,n]\) 第 \(k\) 小的元素,要求使用线段树来实现。 权值线段树 为了解决这个问题,我们引入一个名词: 权值线段树 。那么权值线段树是如何解决上面那个问题的呢? 首先,我们对数组进行离散化处理,离散成为 \([1,n]\) ,然后我们建一颗线段树,线段树的节点存放的即为对应区间的数的个数。 比如数组 \(a={3,3,2,2}\) ,经过离散化后变为 \(2,2,1,1\) 。 对应的线段树即为: 建好线段树之后我们如何求解第 \(k\) 小元素呢?我们从根节点出发,看下它的左儿子的元素个数是否超过了 \(k\) ,如果超过了 \(k\) ,那么第 \(k\) 小一定是左儿子的第 \(k\) 小,我们直接去访问左儿子,否则,假设左儿子的节点为 \(num\) ,那么第 \(k\) 小一定是右儿子的第 \(k-num\) 小,我们去访问右儿子,直到递归终止,我们便找到了第 \(k\) 小元素。 主席树 当我们解决了上一个问题,我们这样考虑: 每输入一个数字 \(a_i\) ,就建一棵 \([1,i]\) 的权值线段树,那么如果要查询 \([l,r

线段树->面积并 Atlantis HDU - 1542

廉价感情. 提交于 2020-01-24 15:23:13
题目链接:https://cn.vjudge.net/problem/HDU-1542 题目大意:求面积并 具体思路:我们首先把矩形分割成一横条一横条的,然后对于每一个我们给定的矩形,我们将储存两个点,一个是左下角,一个是右上角。对于左下角的点,我们将他标记为-1,对于右上角的点,我们把它标记成1,-1代表这块区域的面积是需要减去的,1代表这块区域的面积是需要加上的,然后我们通过扫描线的形式,从y轴从下往上扫就可以了。 离散化的过程,数组还是尽量从0开始吧,因为在更新的过程中会出现0的情况,如果下标从1开始存储的话,会mle的,,,无限递归? ps:这个线段树的区间分开的时候和之前的不太一样,这个线段树{1,5}分开的是{1,3}和{4,5} 注意这个线段树每一个节点代表的是区间,节点1-3代表的是区间1-4,也就是 节点1代表 区间1-2 ,然后我们查询1-4 的时候,直接查询节点1-3就可以了,更新区间1-3的时候,更新节点1-2就可以了。 AC代码: 1 #include <iostream> 2 #include <cstdio> 3 #include <string> 4 #include <cstring> 5 #include <algorithm> 6 #include <cmath> 7 #include <vector> 8 #include <queue> 9

poj 3667 线段树成端更新区间最大连续和

浪子不回头ぞ 提交于 2020-01-24 15:22:38
题目描述: 输入包括n,m,n表示现在一共有n个相邻的房间,m表示有m条命令。 1 a 表示现在来了一个人数为a的队伍,需要一个连续的区间能够容纳这么多人,如果能够容纳,输入最靠左边的房间的号码,否则输出0。 2 a,b 表示清空[a,a+b-1]区间内所有的房子 解题思路: 线段树结构体,定义需要记录的三个值,最大左连续、最大右连续、最大连续。 由上面的图可知,我们现在要更新两端区间,此时,合并区间的最大连续房间数只有三种情况: 1、 左区间最大连续房间数 2、 右区间最大连续房间数 3、 左区间最大右连续房间数 + 右区间最大左连续房间数 只要能够维护这些值,当中的更新和询问就好办了。 代码: View Code 1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 using namespace std; 5 const int N = 50005; 6 int mmax[N<<2],lmax[N<<2],rmax[N<<2],cover[N<<2]; 7 void PushDown(int t,int m) 8 { 9 if(cover[t]!=-1) 10 { 11 cover[t<<1]=cover[t<<1|1]=cover[t]; 12 mmax[t<<1]=lmax[t<<1]=rmax[t

线段树 & 题目

非 Y 不嫁゛ 提交于 2020-01-23 13:08:42
首先说下我写的线段树吧。 我是按照线段树【完全版】那个人的写法来写的,因为网上大多数题解都是按照他的写法来写。 确实比较飘逸,所以就借用了。 节点大小是maxn是4倍,准确来说是大于maxn的2^x次方的最小值的两倍。 lson 和 rson 用宏定义写了。因为是固定的量。 线段树不必保存自身的区间,因为一边传递过去的时候,在函数里就有区间表示,无谓开多些无用的变量。 pushUp函数,更新当前节点cur的值,其实就是,线段树一般都是处理完左右孩子,然后再递归更新父亲的嘛,这个pushUp函数就是用来更新父亲的。感觉不用这个函数更加清楚明了。 pushDown函数,在lazy--upDate的时候有用,作用是把延迟标记更新到左右节点。 多次使用sum不用清0,add要。build的时候就会初始化sum数据。但其他用法就可能要 1 #define lson L, mid, cur << 1 2 #define rson mid + 1, R, cur << 1 | 1 3 void pushUp(int cur) { 4 sum[cur] = sum[cur << 1] + sum[cur << 1 | 1]; 5 } 6 void pushDown(int cur, int total) { 7 if (add[cur]) { 8 add[cur << 1] += add[cur]

习题:V(线段树)

会有一股神秘感。 提交于 2020-01-23 11:09:03
题目 传送门 思路 涉及到区间操作并且是静态的区间,用线段树是再合适不过的 如果直接维护每个点的权值不麻烦 但是要维护历史最大值就十分麻烦 所以我们转化下思路 线段树上维护操作 接着我们思考如何将操作统一化,并且是可叠加的,这样才能方便用懒标记 我们设标记 \((a,b)\) 表示将x变为 \(max(x+a,b)\) 区间加: \((a,-INF)\) 区间赋值: \((-INT,a)\) 将每个值变为 \(max(x_i-a,0)\) 即为: \((-a,0)\) 接着我们考虑合并的问题 假设现在的标记为 \((a,b)\) ,从父亲节点传下来的标记为 \((c,d)\) 先将第一个标记表示出来 \(max(x+a,b)\) ,这就是现在的 \(x'\) , 再将 \(x'\) 代入 现在即为 \(max(c+max(x+a,b),d)\) 可以发现对于前一项的b,b与x无关 而我们定义标记的前一项是与x有关,而后一项与x无关 所以可以将其提取出来,即为 \(max(c+a+x,max(c+b,d))\) 所以合并之后的标记即为 \((a+c,max(b+c),d)\) 现在已经解决了标记合并的问题 现在我们来考虑最大值的维护 如果我们将标记看成一个函数, 那么这个函数的图像一定是这样的: 也就意味这合并之后的标记也是这样子的 同时注意到前面一段的平板是由标记的第二项决定的