线段树

[模板]树链剖分

梦想与她 提交于 2020-01-20 00:34:07
link 树链剖分,感觉是一个很神奇的东西,但是其实并不是那样的 树链剖分其实就是一个线段树 线段树处理的是连续区间,所以当你要加的时候都是连续区间修改 所以可以用轻重链的方式将树分解成为链条,然后用线段树处理 可以很容易看到,为什么用的是dfs但不是用的是bfs呢 因为dfs保持了重链是连续的,所以可以用top[x]记录已x为节点的重链最上方,一个点也包含在重链内 若修改区间为(u,v),但是重链的祖先是一起的,所以当他们的LCA相同时,边break 所以现在u,v是连续的 所以查询(u,v)的简单路径和也就处理了 所以说线段树中可以进行的操作在树上也可以执行了 在处理一个问题 在u的子树上加w 所以修改的区间是u在线段树中的位置$(t)$ 到 $t+size(u)-1$ $size$ 记录以它为根 的子节点个数 $deep(x)$ 深度 $father(x)$ 记录父亲 $son(x)$ 它的重儿子 $top(x)$ 所在重路径的顶部节点 $seg(x)$ x在线段树中的编号 $rev(x)$ 线段树中x的位置所对应的树中节点编号 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; inline int

【Foreign】Weed [线段树]

China☆狼群 提交于 2020-01-19 23:49:53
Weed Time Limit: 20 Sec Memory Limit: 512 MB Description   从前有个栈,一开始是空的。   你写下了 m 个操作,每个操作形如 k v :     若 k = 0,代表往栈顶加入一个数 v     若 k = 1,则代表从栈顶弹出 v 个数,如果栈中的元素少于 v 个,则全部弹出。   接着你又进行了 q 次修改,每次你会选择一个操作,并且修改它的两个参数。   在每次修改后,你都要求出如果依次执行这些操作,最后栈中剩下的元素之和。 Input   第一行两个正整数 m,q,分别表示操作数和修改次数。   接下来 m 行,每行两个整数 k,v,代表一个操作。   接下来 q 行,每行三个正整数 c,k,v,表示将第 c 个操作的参数修改为 k 和 v。 Output   输出 q 行,每行一个整数,代表依次执行所有操作后栈中剩下的元素之和。 Sample Input   5 2   0 3   0 2   0 3   1 1   0 5   1 0 3   1 0 1 Sample Output   10   8 HINT   m,q ≤ 2×1e5, v ≤ 1e4 Solution   首先,我们可以把一个操作拆成: 先删除若干个数,然后加入若干个数 。   那么我们可以用 线段树 来维护,一个节点记录: 删除del个数

[CSP-S模拟测试]:Weed(线段树)

被刻印的时光 ゝ 提交于 2020-01-19 23:48:59
题目描述 $duyege$的电脑上面已经长草了,经过辨认上面有金坷垃的痕迹。 为了查出真相,$duyege$准备修好电脑之后再进行一次金坷垃的模拟实验。 电脑上面有若干层金坷垃,每次只能在上面撒上一层高度为$v_i$的金坷垃,或者除掉最新$v_i$层(不是量)撒的金坷垃。如果上面只留有不足$v_i$层金坷垃,那么就相当于电脑上面没有金坷垃了。 $duyege$非常严谨,一开始先给你$m$个上述操作要你依次完成。然后又对实验步骤进行了$q$次更改,每次更改都会改变其中一个操作为另外一个操作。每次修改之后都会询问最终金坷垃的量有多少。 输入格式 输入第一行为两个正整数$m$、$q$,接下来$m$行每行$2$个整数$k$、$v_i$。$k$为$0$时撒金坷垃,为$1$时除金坷垃。接下来$q$行每行$3$个整数$c_i$、$k$、$v_i$,$c_i$代表被更改的操作是第$c_i$个,后面$2$个数描述更改为这样的操作。 输出格式 输出$q$行代表每次金坷垃的量为多少。 样例 样例输入 10 5 0 10 1 5 0 13 0 18 0 2 1 1 0 8 0 9 1 3 0 7 9 0 3 10 1 7 6 0 8 10 0 5 8 1 2 样例输出 58 0 0 66 41 数据范围与提示 对于$30%$的数据$m\leqslant 1,000,q\leqslant 1,000$。

浅谈线段树

纵然是瞬间 提交于 2020-01-19 23:48:36
博客内容主要来自https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html 感谢原博主大大,代码部分我根据我的习惯进行了更改 数据结构——线段树 1、引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出。 一种回答:这也太简单了,O(n)枚举搜索就行了。 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定。 那好,我再修改一下题目。 B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1、在某个位置加上一个数;2、询问区间[l,r]的和,并输出。 回答:o(n)枚举。 动态修改最起码不能用静态的前缀和做了。 好,我再修改题目: C.给出n个数,n<=1000000,和m个操作,每个操作可能有两种:1、在某个位置加上一个数;2、询问区间[l,r]的和,并输出。 回答:o(n)枚举绝对超时。 再改: D,给出n个数,n<=1000000,和m个操作,每个操作修改一段连续区间[a,b]的值 回答:从a枚举到b,一个一个改。。。。。。有点儿常识的人都知道超时 那怎么办?这就需要一种强大的数据结构: 线段树。 二、基本概念 1、线段树是一棵二叉搜索树,它储存的是一个区间的信息。 2、每个节点以结构体的方式存储,结构体包含以下几个信息: 区间左端点、右端点;(这两者必有) 这个区间要维护的信息

「题目讨论」数据结构

↘锁芯ラ 提交于 2020-01-19 23:43:34
目录 基础数据结构 一般数据结构 进阶数据结构 树状数组 线段树 例题 线段树的合并 平衡树 常用平衡树 重量平衡树 例题 这篇博客主要是扒 \(wzh\) 大佬的课件的,对于一些做题的思路有个人的理解。 基础数据结构 一般数据结构 都进 \(\text{OI}\) 很久了,这些基础数据结构都应该知道: 栈:后进先出的存储结构。 队列:先进先出的存储结构。 堆:结构是完全二叉树,用于支持插入、删除和求最值的基本操作。完成各操作主要通过每个点的权值一定为子树的最值这个性质展开。 左偏树(可并堆):结构是二叉树,是可以合并的堆。因为插入和删除可以用合并实现因而基本操作只有合并。对每个节点维护往右走到叶子的步数 \(dis\) 。并保证 \(dis(left(x))\le dis(right(x))+1\) , \(dis(right(x))\le dis(left(x))\) 。用归纳可以得出树高级别为 \(\mathcal O(\log⁡n)\) 。基于该性质可以保证复杂度,便在合并操作中维护该性质即可。 进阶数据结构 树状数组 对一个序列进行信息维护,支持单点修改、区间查询,要求信息具有可合并性。 一般只对有可减性信息作维护(例如和、异或和)而不对没有可减性信息作维护(例如最值),维护没有可减性信息将增加树状数组的复杂度。 以维护区间和为例,树状数组将记数组 \(C_i=Sum(i

CodeForces 834D The Bakery(线段树优化DP)

我与影子孤独终老i 提交于 2020-01-19 19:50:14
Some time ago Slastyona the Sweetmaid decided to open her own bakery! She bought required ingredients and a wonder-oven which can bake several types of cakes, and opened the bakery. Soon the expenses started to overcome the income, so Slastyona decided to study the sweets market. She learned it's profitable to pack cakes in boxes, and that the more distinct cake types a box contains (let's denote this number as the value of the box), the higher price it has. She needs to change the production technology! The problem is that the oven chooses the cake types on its own and Slastyona can't affect

关于线段树的那些奇技淫巧

和自甴很熟 提交于 2020-01-19 18:17:15
目录:我在右边 关于线段树的那些奇技淫巧 如果你不会线段树, 戳这里 维护区间max/min值: 这就是push_up()浅显易懂. void push_up(int rt) { tree[rt].max = max(tree[lson].max, tree[rson].max); tree[rt].min = min(tree[lson].min, tree[rson].min); } 建树的时候就那样建,push_down的时候看一下max和min都改成lazy就行了. 有的时候用不到push_down(); 区间查询max/min值 也很好懂,和求区间和差不多. 有修改操作的时候可以加上pushd_down(); int query_max(int rt, int l, int r, int L, int R) { if (L <= l && r <= R) return tree[rt].max; int mid = (l + r) >> 1, maxn = -1; if (L <= mid) maxn = max(maxn, query_max(lson, l, mid, L, R)); if (R > mid) maxn = max(maxn, query_max(rson, mid + 1, r, L, R)); return maxn; } 维护区间和+区间开平方.

树状数组学习笔记

做~自己de王妃 提交于 2020-01-19 00:16:59
本文是笔者学完树状数组后对树状数组进行的一个学习总结,如有纰漏或者错误之处,还望读者不吝指教,不胜感激! 一、树状数组的概念: 所谓树状数组(Binary Indexed Tree),从字面意思来讲,就是用数组来模拟树形结构。也就是说它可以将线性结构转化为树形结构,从而实现跳跃式的扫描。所以它一般应用于解决动态前缀和问题。 二、树状数组一般可以解决的问题: 树状数组可以解决大部分基于区间上的更新和求和问题。但功能有限,遇到一般的复杂问题是不能解决的。 三、和线段树的区别: 所有可以用树状数组解决的问题都可以用线段树解决。但树状数组的代码复杂度明显优于线段树。所以可以使用树状数组解决的问题都可以尽量考虑用树状数组解决。(当然,神牛请随意) 四、时间复杂度和空间复杂度: 树状数组修改和查询的时间复杂度都是O(logN),空间复杂度为O(N) 下面我将从树状数组的创建到树状数组可以实现的各个功能开始逐一讲解。 1、树状数组的创建: 讲解二叉树的结构之前,我先引入二叉树的结构,如图下图所示: 这样每一个父亲节点都存的是两个子节点的值,那就可以解决一般的基于区间的查询和修改问题,但这样的树形结构是线段树,不是树状数组。所以树状数组是一个什么样的树形结构呢? 首先,我们把二叉树的结构变形一下: 之后,在删掉部分结点,如下图所示: 黑色数组表示原来的数组A[i],红色代表树状数组C[i],看上图

线段树个人理解及模板

旧街凉风 提交于 2020-01-18 14:59:46
一.线段树的相关定义及用途  (1)线段树的定义     线段树是一种可以加快对区间进行更新以及查询的一种树状结构,类似于将一个区间的及其子区间的相关信息存储在一颗二叉树中。 (2)线段树的用途大致为以下几种     1>某个子区间进行区间更新     2>查询某个子区间的总和     3>查询某个子区间的最值 二.线段树的建立   (1)节点的内容(一个节点代表一个区间)       1>NodeLeft-----该区间的左边界       2>NodeRight-----该区间的右边界       3>NodeMin--------该区间的最小值       4>NodeMax------该区间的最大值       5>NodeSum------该区间的总和   (2)利用节点之间的下标表示它们所代表的区间之间的关系      1>设父节点的编号为n,则其左半区间的编号为2*n,右半区间的编号为2*n+1       2>设某个非总区间的编号为n,则其父区间编号为n/2       三.线段树的实例使用及代码   (1)实例背景    (2)建树       1>将7个红包建立为一颗线段树            2>建树代码 struct Tree {//定义结构 ll l,r;//节点左右端点 ll sum;//求和 ll lazy;//延迟标记 ll maxn;//最大值

IOI 2007 Sail (线段树+贪心)

不问归期 提交于 2020-01-18 11:13:54
题意: 有一艘船,船上有 \(n\) 个旗杆,每个旗杆上有 \(h_i\) 个小节。每根旗杆上会挂 \(k_i\) 张帆 每个小节最多挂一个帆。在风中,帆的不同排布方式会产生不同的推动力 对于任意一张帆,他的推动力折扣等于再它后面并且和它在同一高度的帆的数目 所有帆的任意一种位置组合的推动力折扣和等于在该位置下所有帆的推动力折扣的和 求所有位置组合最小的推动力折扣和 显然有一种贪心方案: 设每个位置上的旗子数量为 \(s_i\) ,那么按照旗杆长度从小到大排序 每次贪心的找到可行的位置中,旗子数量最少的 \(k\) 个放旗子即可(即 \(s_i+1\) ) 最后 \(\sum\limits_{i}\frac{s_i(s_i-1)}{2}\) 就是答案了。 时间复杂度 \(O(\sum\limits_{i=1}^nh_i)\) 时间复杂度不行,如何优化? 按照 \(s_i\) 维护一个排序后的数列,相当于每次给区间加 \(1\) 区间加 \(1\) 可能使得区间不再有序! 原来的 \(s_i:~[1,1,1,1,3]\) \([1,3]\) 加 \(1\) 后的数列应为: \([1,2,2,2,3]\) 实际上也是区间加法!我们只要找到之后第一个比之前这个数大的即可 可以利用线段树优化,时间复杂度 \(O(n~log~n)\) #include <map> #include <set