线段树

2019 ICPC Asia Yinchuan Regional G. Pot!!

感情迁移 提交于 2020-01-26 22:54:58
2019 ICPC Asia Yinchuan Regional 【题目大意】 略 【解题思路】 在线段树上打4个标记分别表示将节点权值分解为2,4,5,7的个数,或者开4颗线段树也可 【AC代码】 # include <cstdio> # include <cstring> # include <iostream> # include <algorithm> using namespace std ; typedef long long ll ; const int maxn = 1e6 + 10 ; inline ll max ( ll a , ll b ) { return a > b ? a : b ; } struct Tree { int l , r ; ll flag2 ; ll flag3 ; ll flag5 ; ll flag7 ; ll lazy [ 5 ] ; } tree [ maxn ] ; inline void pushdown ( int root ) { tree [ root ] . flag2 + = tree [ root ] . lazy [ 1 ] ; tree [ root ] . flag3 + = tree [ root ] . lazy [ 2 ] ; tree [ root ] . flag5 + = tree [ root

线段树

余生长醉 提交于 2020-01-26 16:44:08
线段树 1、线段树是一棵二叉搜索树,它储存的是一个区间的信息。 2、每个节点以结构体的方式存储,结构体包含以下几个信息: 区间左端点、右端点;(这两者必有) 这个区间要维护的信息(事实际情况而定,数目不等)。 3、线段树的基本思想: 二分 。 4、线段树一般结构如图所示: 每个节点的左孩子区间范围为[l,mid],右孩子为[mid+1,r] 线段树的基础操作主要有5个: 建树、单点查询、单点修改、区间查询、区间修改 。 (以下均为求和操作) 1 struct node{ 2 int l,r,w;//l左区间,r右区间,w区间和 3 }xdtree[4*n];//n个数 一、建树 a、对于二分到的每一个结点,给它的左右端点确定范围。 b、如果是叶子节点,存储要维护的信息。 c、状态合并。 !!:: 4倍空间 不要漏了return语句 1 void build(int l,int r,int k){//k是根节点的下标 2 3 xdtree[k].l=l; 4 xdtree[k].r=r; 5 6 if(l==r){ 7 8 cout<<xdtree[k].w;//n个数的和 9 return; 10 } 11 12 int m=(l+r)/2; 13 build(l,m,2*k); 14 build(m+1,r,2*k+1); 15 xdtree[k].w=xdtree[2*k].w

浅谈ZKW线段树之lazy标记

Deadly 提交于 2020-01-26 16:27:29
zkw线段树 的网上讲解似乎很多,我就不介绍大纲了。 zkw线段树 的核心思想就是尽量 省去递归 来减小常数 比如在 build 操作 普通线段树: void build(int p,int l,int r) { if (l==r){tree[p]=a[l];return;} int mid=(l+r)>>1,ls=p<<1,rs=p|1; build(ls,l,mid),build(rs,mid+1,r); updata(i); } zkw线段树: void build() { for (m=1;m<n;m<<=1);//--m; for (int i=1;i<=n;++i) t[i+m]=read(); for (int i=m;i;--i) updata(i); } 常数又小又简短 PS:上面注释部分可加可不加,不加维护 \([0,n]\) ,加了维护 \([1,n]\) ,想省事就别加了; 然而我想说的重点不在这里,如果你想学zkw线段树请右转zkw的论文 《统计的力量》 , 下面我将介绍如何在zkw线段树中使用 \(lazy\) 标记。 我们都知道如何在线段树上打标记。 (没学过lazy标记就不要点进来) 我们只需要在对应区间打标记,这和普通线段树没什么区别。 但维护标记就比较麻烦了,这里有一种非常简单的维护方法 一般的zkw操作都是这样的 void change(int

线段树基础详解

空扰寡人 提交于 2020-01-26 15:58:05
转载自: 点击打开链接 (基础版) 进阶版 基本概念: 存储结构是怎样的? 线段树是一种二叉树,当然可以像一般的树那样写成结构体,指针什么的。 但是它的优点是,它也可以用数组来实现树形结构,可以大大简化代码。 数组形式适合在编程竞赛中使用,在已经知道线段树的最大规模的情况下,直接开足够空间的数组,然后在上面建立线段树。 简单的记法: 足够的空间 = 数组大小n的四倍。 实际上足够的空间 = (n向上扩充到最近的2的某个次方)的两倍。 举例子:假设数组长度为5,就需要5先扩充成8,8*2=16.线段树需要16个元素。如果数组元素为8,那么也需要16个元素。 所以线段树需要的空间是n的两倍到四倍之间的某个数,一般就开4*n的空间就好,如果空间不够,可以自己算好最大值来省点空间。 怎么用数组来表示一颗二叉树呢?假设某个节点的编号为v,那么它的左子节点编号为2*v,右子节点编号为2*v+1。 然后规定根节点为1.这样一颗二叉树就构造完成了。通常2*v在代码中写成 v<<1 。 2*v+1写成 v<<1|1 。 以下以维护数列区间和的线段树为例,演示最基本的线段树代码。 (0)定义: #define maxn 100007 // 元素总个数 #define ls l,m,rt<<1 #define rs m+1,r,rt<<1|1 int Sum[maxn<<2],Add[maxn<<2];

线段树离散化+区间更新——cf1179C好题

℡╲_俬逩灬. 提交于 2020-01-26 15:46:51
绝对是很好的题 把问题转化成当第i个询问的答案是数值x时是否可行 要判断值x是否可行,只要再将问题转化成a数组里>=x的值数量是否严格大于b数组里的>=x的值 那么线段树叶子结点维护对于值x的a数组里的合法数数量-b数组里的合法数数量,如果是正数即这个值可行 线段树维护区间最大值,然后询问最靠右的非负叶子下标 #include<bits/stdc++.h> #include<vector> using namespace std; #define maxn 1000005 int Q,n,m,a[maxn],b[maxn],ans[maxn]; struct Query{int op,i,x;}q[maxn]; vector<int>v; #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 int lazy[maxn<<2],Max[maxn<<2]; void pushdown(int rt){ if(lazy[rt]!=0){ Max[rt<<1]+=lazy[rt]; Max[rt<<1|1]+=lazy[rt]; lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } void pushup(int rt){ Max[rt]=max(Max[rt<<1]

线段树

孤街醉人 提交于 2020-01-26 15:45:49
一、中心思想 二分, 每查找一个区间或者修改一个区间的时候,一层一层往下二分,找到 区间的左端点和右端点是不是在应该修改的区间内,然后进行修改操作... 二、基本操作 1、建树 首先你要弄明白这棵线段树是干什么的,即这棵线段树的节点要存储哪些信息 如果你不知道线段树里要存储哪些信息,回答以下三个问题: ①对于要求的输出,需要维护哪些信息(线段树节点里要存什么)? ②这些信息是否可以直接得到? ③如果不能,需要维护哪些信息来间接获得? 然后我们要做的事情就是: 两件事:由上而下干什么? 由下而上干什么? 由上而下的过程——初始化 由下而上的过程——合并区间信息 从上往下跑一圈又必须要倒回去的算法——递归。 那么就是递归实现的喽。 要开四倍空间 来自沙雕网友的解答: 理论上是2n-1的空间,但是你递归建立的时候当前节点为r ,那么左右孩子分别是2 r,2 r+1,此时编译器并不知道递归已结束, 因为你的结束条件是在递归之前的,所以编译器会认为下标访问出错,也就是空间开小了, 应该再开大2倍。有时候可能你发现开2,3倍的空间也可以AC,那只是因为测试数据并没有那么大。 主要的思路就是递归加二分 看看代码吧(~~怀念那学长看着我打代码,强行改我码风的日子) void build(ll l,ll r,ll rt) { t[rt].len = r - l + 1; if(l == r) { t

可持久化线段树——区间更新hdu4348

北慕城南 提交于 2020-01-26 15:45:13
和线段树类似,每个结点也要打lazy标记 但是lazy标记和线段树不一样 具体区别在于 可持久化后lazy-tag不用往下传递,而是固定在这个区间并不断累加,变成了这个区间固有的性质(有点像分块的标记了) update就按照这么来 int update(int last,int L,int R,int c,int l,int r){ int now=++size; T[now]=T[last]; if(L<=l && R>=r){ T[now].sum+=(r-l+1)*c; T[now].add+=c; return now; } int mid=l+r>>1; if(L<=mid)T[now].lc=update(T[last].lc,L,R,c,l,mid); if(R>mid)T[now].rc=update(T[last].rc,L,R,c,mid+1,r); pushup(l,r,now); return now; } 查询时由于lazytag固定在区间上。所以向下查询的时候要把上层的lazytag的影响都算上,即递归时传递一个上层区间的 影响值(例如add) ll query(int now,int L,int R,int add,int l,int r){ if(L<=l && R>=r) return T[now].sum+(ll)add*(r-l+1); int

线段树数据结构详解

邮差的信 提交于 2020-01-26 15:43:53
线段树数据结构详解 Coded by Jelly_Goat. All rights reserved. 这一部分是线段树。 线段树,顾名思义,是一种树形数据结构,适用于各种求区间统一算法的动静两平衡的数据结构。 这里什么是统一算法? (自己口胡的统一算法) 比如求最大值or最小值、区间求和,一样的区间都是一样的算法,这也是和动态dp不同的地方。 前置知识1:二叉搜索树 二叉搜索树就是根节点比左儿子大,比右儿子小的一种二叉树。 前置知识2:向量存储 向量存储是用来存完全二叉树儿子和父亲关系的。 如果不满足,我们还可以用链式前向星存 举个例子: 有一颗完全二叉树,节点数是16,然后你会发现: lson标号=root标号*2 , rson标号=root标号*2+1 。 显然可见不是偶然,是二叉树满了导致的。 那么我们可以用下标表示存储的线段树节点。 例如: tree[100] 就是 tree[200] 和 tree[201] 的root。 今天只讨论最普通的线段树(板子:求和) 操作1:建树 怎样种一棵线段树? Jelly_Goat:需要一条线段 没问题,真的需要原序列。 从上往下二分区间长度,递归建树。 代码示范: //维护根节点的和 inline void update(int rt) { tree[rt].sum=tree[rt*2].sum+tree[rt*2+1].sum; }

线段树入门教程

狂风中的少年 提交于 2020-01-26 15:42:40
线段树入门教程 线段树往往会是各位OIer接触的第一种玄学数据结构,awa这东西很不好理解,但确实很有用。我还是争取写一篇对刚入门的新手友好的文章对线段树加一说明,手把手教大家写线段树。 线段树是什么? 二叉树大家知道吗?就是每一个节点会有左右两个子节点,子节点又有子节点……总起来就是二叉树。二叉树在玄学数据结构中会经常用到,比如splay,treap,乃至红黑树等等魔法玩意。这些不用管,就了解一下二叉树就好了。 很好理解对吗? 线段树就是基于二叉树的一种数据结构,用于解决在一段区间上修改和查询的问题。 画一张易于理解的图 好吧我承认图画的吃藕。。蓝色是小标号,忽略就好了。 线段树的本质,就是将一段区间(图中的1~8)经过多次二分,拆成一个一个的单点(图中的1 2 3 4 5 6 7 8) 嗯没错这个就是线段树 为什么选择线段树? 因为快。。。 举个例子,比如我们要将2~5号点加上1,朴素做法是一个一个相加,时间复杂度为O(n),而我们如果使用线段树,会是这样操作的: 我们从线段树的顶端开始; 如果当前枚举到的区间被要加v的区间完全包含,就在这个区间进行加法操作,把这个区间加上要加的数v乘上这段区间的元素(点)个数,再记录一下这段区间被加过v,就不再往下枚举了。 如果不被完全包含,就接着二分,枚举当前这一段的前半段和后半段 这个就是线段树的原理辣,努力理解一下。 还是拿1

线段树(segment tree)

自闭症网瘾萝莉.ら 提交于 2020-01-26 15:41:31
这是从网上看到的两篇博客感觉挺好的就转过来,留作复习的材料。 转载来源: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]内所有元素的和。如何设计算法