线段树

线段树总结

こ雲淡風輕ζ 提交于 2019-11-29 08:31:55
参考题目:A Simple Problem with Integers POJ-3468 距离上一次写这道题已经过去两个月了,前天打模拟赛时连线段树都手敲不出来了。所以这次重新来复习一下线段树。 这次主要是记录一下对线段树区间修改的理解: 一开始我们先是理解线段树的建树原理以及查询原理。利用一个二叉树建立,每个父节点可以记录左右子结点的和或者最大值,以此来维护区间内容。 查询也类似,通过递归寻找,找到所有符合条件(区间内深度最浅)结点,然后将其求和返回或者求最大值返回。 而在之后有单点修改。单点修改重建树来理解,就是递归,找到范围(l,r) ,l=r 时的结点位置更新并 push_up() 向上建树。 但是对于大量数据查询,以及面对区间修改问题时,要一个一个找到位置再更新push_up()就会非常费时间。 所以我们试想能否使用一个标记 tag 传递,在更新范围内的结点就把 tag 传递下去,然后对所有含有tag 的叶子结点更新,再push_up(); 但是对于上面实际上还有更加优化的方案:即 lazy[] 标记。我们用 lazy[]标记记录每个结点修改信息,就像上面一样。但是当递归到某个完全被包含于查询区间的 部分时,我们直接对这个区间更新 即 (r-l+1) * lazy[p] . (完全被包含,所以直接乘以长度即可),单接下来我就直接返回了,不往下递归了

主席树学习笔记

谁都会走 提交于 2019-11-29 08:29:32
感性的理解,主席树就是多个阶段下(即可保存多个版本)的多棵线段树(可以是普通线段树也可以是权值线段树)。 一、权值线段树 普通线段树相信大家都会,这里先简述一下权值线段树。 顾名思义,权值线段树的每个节点带的[l,r]表示的是权值区间l~r,而不是像普通平衡树上那样表示在序列上的第l个位置~第r个位置。而每个节点的val值表示该权值区间内有多少个数。比如对于1,4,1,6,4,4,2这样一个序列,可以表示为下图: 对于插入操作,直接从根节点往下找到所有权值范围包括k的节点,将其val++,一直到叶子节点。 对于查询操作,如查询第k小的数,即从根节点开始,若 左子树的val<=k ,就往左子树继续查找,否则将 k-=左子树的val 并往右子树查找(显然该范围的第k小就是其右子范围的第 k-tre[tre[x].ls].val 小)。 应该是比较好理解的。 但是,有一个很严重的问题,比如说1,10^6,10^7这样一个序列,明明只有三个元素,但是对于权值线段树,我们是要开 2*(10^7) 的节点的,显然空间上是无法接受的。怎么办呢? 这里讲的是 离散化+动态开点。 首先离散化,就是用每个权值在序列中的排名来代替该权值在序列中的位置。 其次是动态开点。在普通线段树中,我们建树、插入、查询的时候都是直接走的 rt<<1 或者 rt<<1|1 ,但是我们发现,有些节点是不需要的

【线段树 dp】8.6集合

こ雲淡風輕ζ 提交于 2019-11-29 08:27:46
线段树维护dp 题目大意 给定初始大小为 $N$ 的正整数集合。定义两个数$x$和$y$建立联系的的代价为 $|x-y|$。我们定义整数集合的代价为:将每个整数都与至少一个另外的整数建立联系之后,所有联系的最小代价之和。如果集合大小小于等于1,则代价为 0。 要求动态维护这个正整数集合的代价。 $n \le 200000$ 题目分析 常规的线段树维护dp 考虑在权值上处理这个问题:对于一个区间$[l,r]$,在$(l,r)$中的数肯定是自身配对了才优。也就是说一个区间可以被概括成四个状态:$00,01,10,11$其中$0$表示这一个端点暂时没有配对,$1$表示这个端点已经内部配对了。 接下去就是常规的权值线段树处理dp 1 #include<bits/stdc++.h> 2 typedef long long ll; 3 const ll INF = 1ll<<50; 4 const int maxn = 200035; 5 const int LIM = 1000000000; 6 const int maxNode = 8000035; 7 8 struct node 9 { 10 int mn,mx,ls,rs,val; 11 ll f00,f01,f10,f11; 12 }f[maxNode]; 13 int n,m,rt,tot,a[maxn]; 14 int stk

XKC's basketball team【线段树查询】

天大地大妈咪最大 提交于 2019-11-29 08:24:55
XKC , the captain of the basketball team , is directing a train of n n team members. He makes all members stand in a row , and numbers them 1 \cdots n 1 ⋯ n from left to right. The ability of the i i-th person is w_i w i ​ , and if there is a guy whose ability is not less than w_i+m w i ​ + m stands on his right , he will become angry. It means that the j j-th person will make the i i-th person angry if j>i j > i and w_j \ge w_i+m w j ​ ≥ w i ​ + m. We define the anger of the i i-th person as the number of people between him and the person , who makes him angry and the distance from him is the

I Hate It HDU - 1754(线段树找区间最大值)

橙三吉。 提交于 2019-11-29 07:10:28
I Hate It HDU - 1754 题目链接: https://vjudge.net/problem/HDU-1754 题目: 很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。 这让很多学生很反感。 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。 Input本题目包含多组测试,请处理到文件结束。 在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。 学生ID编号分别从1编到N。 第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。 接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。 当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。 当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。 Output对于每一次询问操作,在一行里面输出最高成绩。Sample Input 5 6 1 2 3 4 5 Q 1 5 U 3 6 Q 3 4 Q 4 5 U 2 9 Q 1 5 Sample Output 5 6 5 9 思路:简单修改一下线段树单点修改即可

线段树模板

[亡魂溺海] 提交于 2019-11-29 07:05:16
不要当线段树都不会敲的菜鸡了。 线段树所要提供的是查询一个区间 内的信息 ,并允许修改操作。 节点数据向上更新 对于区间求和: void push_up(int rt){ tree[rt] = tree[rt<<1] + tree[rt<<1|1]; } 对于区间求最值: void push_up(int rt){ tree[rt] = max(tree[rt<<1],tree[rt<<1|1]); } 节点懒惰标记向下传递 对于区间求和: void push_down(int rt,int len){ tree[rt<<1] += lazy[rt]*( len-(len>>1) ); lazy[rt<<1] += lazy[rt]; tree[rt<<1|1] += lazy[rt]*(len>>1); lazy[rt<<1|1] += lazy[rt]; lazy[rt]=0; } 对于区间求最值: void push_down(int rt){ tree[rt<<1] += lazy[rt]; lazy[rt<<1] += lazy[rt]; tree[rt<<1|1] += lazy[rt]; lazy[rt<<1|1] += lazy[rt]; lazy[rt] = 0; } 建树 void build(int l,int r,int rt){ lazy[rt] = 0;

Buses and People + CodeForces - 160E

你离开我真会死。 提交于 2019-11-29 06:55:02
Buses and People 三维处理 题目大意:有n辆公交车,每辆公交车有s(起始点),f(终点),t(发车时间) ,有m个人,每个人有l(起点),r(终点),t(出现时间) 对于一个人,当t(人)<=t(车), s<=l,r <=f 时就能上这辆车,问能上的发车时间最早的是那一辆 类似三维偏序问题,有三个维度需要我们解决。首先,车和人都有l,r,t,三个属性,先按照l排序,解决第一维,这样,对于一个人,前面的所有的车都是可能上去的。剩下的两个维度用线段树来解决,线段树维护区间最大值。线段树下标对应的是t,维护的最大值是r。把车和人在一起排序,从头遍历,是车,就在t位置插入r,是人,就在线段树t位置到末端查询满足小于r的最小的下标对应的id(也就是车的id)。 怎么也没想到可以用一个线段树直接解决两维。 查询到一个人,前面已经插入的车的l一定都比它小了;线段树查询的是t到末端,t大于人的车一定在其中;线段树维护的是r最大值,只要查询的区间内的r有大于等于此人的r,那么这个人就一定能上车。 排序解决一维,线段树解决一维,维护的最大值解决一维。 # include <cstdio> # include <algorithm> # include <cmath> # define Mid ((a[k].l+a[k].r)>>1) # define cl (k<<1) #

线段树

亡梦爱人 提交于 2019-11-29 06:04:49
基础知识: 线段树是一颗 二叉树搜索树 ,也是 二叉平衡树 。 根区间:[L,R] 左孩子区间:[L, (L+R)/2] 右孩子区间:[(L+R)/2+1, R] 叶子节点:L=R 树高logN 树上的操作都和树高有关系,例如:优先队列,堆等 时间复杂度O(longn) 用法 : 区间更新 区间查询 例如区间最值查询(Rang Minimum/Maximum Query, RMQ ) 树状数组 擅长 点更新 和 区间和查询、 区间更新不擅长,没有加法关系树状数组就不适合。 线段树操作一. RMQ : 点更新:修改一个元素的值 区间查询:查询一个区间的最值(最大或者最小) 除了最后一层中间都是满二叉树,满二叉树可顺序存储,顺序存储可以直接定位。 tree[] k / \ 2k 2k+1 n 个节点需要空间是 4*n 区间跟新打标记 点更新,区间查询,没有区间更新。 1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 5 using namespace std; 6 7 const int maxn = 1000005; 8 const int inf = 0x3f3f3f3f; 9 int n, a[maxn]; 10 11 struct node // 结点 12 { 13 int l, r, mx

Color the ball HDU - 1556 (线段树)

本秂侑毒 提交于 2019-11-29 05:58:40
思路:线段树,区间更新 1 #include<iostream> 2 #include<vector> 3 #include<string> 4 #include<cmath> 5 #include<set> 6 #include<algorithm> 7 #include<cstdio> 8 #include<map> 9 #include<cstring> 10 #include<list> 11 12 #define MAXSIZE 100010 13 14 using namespace std; 15 16 int tree[MAXSIZE*4]; 17 int lz[MAXSIZE*4]; 18 int N; 19 int cnt = 0; // 控制输出的打印格式 20 21 22 void init() 23 { 24 memset(tree, 0, sizeof(tree)); 25 memset(lz, 0, sizeof(lz)); 26 } 27 28 29 void build(int node, int l, int r) 30 { 31 if(l == r) 32 { 33 tree[node] = 0; 34 return; 35 } 36 int mid = (l+r)/2; 37 build(node*2, l, mid); 38 build

[CF377D][线段树][扫描线]Developing Game

孤者浪人 提交于 2019-11-29 05:44:50
CF377D 把 l , r l,r l , r 看成两维坐标,假设最后有解,那一定存在一个 ( L , R ) (L,R) ( L , R ) 使得 L ≥ m a x { l [ i ] } , L ≤ m i n { v [ i ] } L\ge max\{l[i]\},L\le min\{v[i]\} L ≥ m a x { l [ i ] } , L ≤ m i n { v [ i ] } 且 R ≥ m a x { v [ i ] } , R ≤ m i n { r [ i ] } R\ge max\{v[i]\},R\le min\{r[i]\} R ≥ m a x { v [ i ] } , R ≤ m i n { r [ i ] } ,否则显然不满足条件 即是把 l , v , r l,v,r l , v , r 看作一个横坐标 ( l , v ) (l,v) ( l , v ) 纵坐标 ( v , r ) (v,r) ( v , r ) 的矩形,要求尽量多的矩形使得它们的面积并不为空(可以为0,就是矩形并为一个点的情况) 那就可以线段树+扫描线了 Code: # include <bits/stdc++.h> # define pb push_back using namespace std ; inline int read ( ) { int res = 0