线段树

学习笔记:可持久化线段树(主席树):静态 + 动态

我们两清 提交于 2019-11-28 21:03:26
学习笔记:可持久化线段树(主席树):静态 + 动态 前置知识: 线段树。线段树分享可以看: @秦淮岸 、 @ZYzzz 、 @妄想の岚がそこに 树状数组。 \(BIT\) 分享可以看: @T-Sherlock 、 Chicago 、 @weishengkun 权值线段树:相当于将线段树当成一个 桶 ,其中的每一个点所代表的区间相当于 一段值域 。维护的值为这段值域中的一些信息。 例如该图,节点 \(2\) 代表的是值域为 \([1, 2]\) 的区间,节点 \(6\) 代表值域为 \([3, 4]\) 的区间... 可持久化概念: 可持久化实质上就是存储该数据结构 所有的历史状态 ,以达到高效的处理某些信息的目的。 静态区间第 \(k\) 小 抛出问题 题目链接 :给定长度为 \(N\) 的序列 \(A\) ,有 \(M\) 次询问,给定 \(l_i, r_i, k_i\) ,求在 \([l_i, r_i]\) 区间内第 \(k_i\) 小的数是多少。 \(N <= 10^5, M <= 10^4\) 先考虑如何求总序列第 \(k\) 小 我们可以建立一颗权值线段树,每个点存储的信息为 该值域区间存在的数的个数 。 因为线段树的性质,所以每个点的左子树的值域区间 $ <= $ 右子树的值域区间。 所以我们先看左子树区间有多少个数,记为 \(cnt_{left}\) 。 如果 \(k

猫树总结

微笑、不失礼 提交于 2019-11-28 20:36:46
猫树 这个时候就要 \(\texttt{orz immortalCO}\) 了。 猫锟的 问题描述 现在你有一些无修改的信息,然后有多组询问,每一次对区间进行询问,信息满足结合律和可交换性。 解法 我们定义一种算法的复杂度为 \(O(A)+O(B)+O(C)\) ,分别表示预处理复杂度,单次询问复杂度和空间复杂度。 可减信息 直接数组维护。 \(O(n)+O(1)+O(n)\) 区间最值 倍增的 \(RMQ\) 解决即可。 \(O(nlogn)+O(1)+O(nlogn)\) 只支持结合律和可交换性 一般采用线段树。 \(O(n)+O(logn)+O(n)\) 一种新的解法:猫树 我们仔细思考一下,对于一个点 \(o\) 而言,如果他需要递归两个儿子等同于 \(L\le l \le mid \le r \le R\) 。 因为信息满足结合律和可交换律,所以我们只需要对于两边快速合并即可。 然后对于每一层我们可以快速求出点 \(i\) 对于 \(mid\) ( \(i<mid\) )以及 \((i \ge mid)\) 的答案。 此时唯一需要的就是快速定位点 \(o\) ,如果按照一般的线段树建树显然不好定位,但是如果我们建一棵 \(2^{k}\) 的树,就很容易定位了。 这时我们便找到了一个新的算法:猫树, \(O(nlogn)+O(1)+O(nlogn)\) 看上去很棒!

cf 1208D 树状数组+倍增/线段树

笑着哭i 提交于 2019-11-28 19:19:13
解法一 树状数组: ```math 倒序,对于每个s_{i},找出从1-n中的未被利用且和为s_{i}的前缀和,则p_{i}为这些数中最大的数+1,每次找都后需要及时删去 此处查找使用倍增的方法。 数组数组+倍增,单次操作O(log n) 树状数组+二分,单次操作O(log n*log n) #include <bits/stdc++.h> using namespace std; #define ll long long ll s[200050], c[200050]; ll p[25]; int n, t, h[200050]; void add(int x, int t) { while (x <= n) { c[x] -= t; x += x&-x; } } int main() { p[0] = 1; for (int i = 1; i<22; i++)p[i] = p[i - 1] << 1; scanf("%d",&n); t = log(n) / log(2); for (int i = 1; i <= n; i++) { scanf("%lld",&s[i]); c[i] += i; if (i + (i&-i) <= n)c[i + (i&-i)] += c[i]; } // for(int i=1;i<=n;i++) // cout<<c[i]<<" "; /

P3582 [POI2015]KIN 线段树

為{幸葍}努か 提交于 2019-11-28 18:17:17
   题目描述 共有m部电影,编号为1~m,第i部电影的好看值为w[i]。在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。 输入格式 第一行两个整数n,m(1<=m<=n<=1000000)。第二行包含n个整数f[1],f[2],…,f n 。第三行包含m个整数w[1],w[2],…,w m 。 输出格式 输出观看且仅观看过一次的电影的好看值的总和的最大值。 输入输出样例 输入 #1 复制 9 4 2 3 1 1 4 1 2 4 1 5 3 6 6 输出 #1 复制 15 说明/提示 共有m部电影,编号为1~m,第i部电影的好看值为w[i]。 在n天之中(从1~n编号)每天会放映一部电影,第i天放映的是第f[i]部。 你可以选择l,r(1<=l<=r<=n),并观看第l,l+1,…,r天内所有的电影。如果同一部电影你观看多于一次,你会感到无聊,于是无法获得这部电影的好看值。所以你希望最大化观看且仅观看过一次的电影的好看值的总和。 有两种写法: https://www.luogu.org/problemnew/solution/P3582

[SDOI2011] 染色 题解

旧街凉风 提交于 2019-11-28 16:40:29
题解: 先考虑在在区间上如何做这个操作。考虑两个相邻的区间A,B,不妨设区间A在区间B的左端,设区间A的颜色段数量为 $sum_A$ 区间B的颜色段数量为 $sum_B$,那么将区间A和B合并后颜色段的数量是否是 $sum_A+sum_B$ 呢?显然不是,如果区间A的右端和区间B的左端颜色相同的话,答案应该是 $sum_A+sum_B -1$,画个图很好理解。合并后的大区间的左端颜色显然和区间A的左端颜色相同,大区间的右端颜色和B的右端颜色相同。那么显然合并这个过程是满足“区间可加性”的,于是我们可以用线段树来维护某一段区间的左端颜色、右端颜色与颜色段。 那么问题来了,现在并不是在区间上,而是在树上!这时候就是我们的“ 树链剖分 ”大展身手的时刻了!树链剖分能将树分成一条一条链,且链上编号是连续的,很方便我们用线段树来维护。 附上代码(注释很详细哦): 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=200000+5; 4 5 //struct数组存线段树,lson与rson代表左右区间,lco与rco代表左右两端的颜色 6 //sum代表区间颜色块的数量 7 struct SegementTree{ 8 int lson,rson,lco,rco,sum; 9 }t[4*N]; 10 11 int c

hdu 6703 array(权值线段树)

让人想犯罪 __ 提交于 2019-11-28 16:21:24
Problem Description You are given an array a 1 , a 2 , . . . , a n ( ∀ i ∈ [ 1 , n ] , 1 ≤ a i ≤ n ) . Initially, each element of the array is **unique**. Moreover, there are m instructions. Each instruction is in one of the following two formats: 1. ( 1 , p o s ) ,indicating to change the value of a p o s to a p o s + 10 , 000 , 000 ; 2. ( 2 , r , k ) ,indicating to ask the minimum value which is **not equal** to any a i ( 1 ≤ i ≤ r ) and **not less ** than k . Please print all results of the instructions in format 2 . Input The first line of the input contains an integer T ( 1 ≤ T ≤ 10 ) ,

模板 - 线段树套平衡树

回眸只為那壹抹淺笑 提交于 2019-11-28 15:54:07
一种可以在区间上找名次的数据结构。 #include<bits/stdc++.h> using namespace std; typedef long long ll; namespace Treap { #define ls ch[id][0] #define rs ch[id][1] const int INF = 2147483647; const int N = 5e4 + 5; const int MAXN = 25 * N; //每个元素会被覆盖若干次线段树有log层,每层的平衡树的长度和都是n,按道理说是精确的nlogn,但这里还是开尽可能大 int ch[MAXN][2], dat[MAXN]; int val[MAXN]; int cnt[MAXN]; int siz[MAXN]; int tot; inline void Init() { tot = 0; } inline int NewNode(int v, int num) { int id = ++tot; ls = rs = 0; dat[id] = rand(); val[id] = v; cnt[id] = num; siz[id] = num; return id; } inline void PushUp(int id) { siz[id] = siz[ls] + siz[rs] + cnt

HDU1166 敌兵布阵---线段树

房东的猫 提交于 2019-11-28 15:49:16
普通线段树,同时实现加,减,最大值操作 #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define ll long long using namespace std; const int maxn=2e5+5; int A[maxn]; int tree[maxn<<2]; int n,m; int L,R,C; void pushup(int rt){ tree[rt]=tree[rt<<1]+tree[rt<<1|1];//因为不是求和,所以直接获取左右结点中大的数 } void build(int l,int r,int rt){ if(l==r){ tree[rt]=A[l]; return; } int m=(l+r)>>1;//线段树的平分方式 build(l,m,rt<<1);//左边,左子结点 build(m+1,r,rt<<1|1);//右边,右子结点 pushup(rt); } void update(int l,int r,int rt){ if(l==r){ tree[rt]+=C; return; } int m=(l+r)>>1; if(L<=m)update(l,m,rt<<1);//更新在左子树 else update(m+1,r,rt<

线段树

假如想象 提交于 2019-11-28 15:25:13
线段树 线段树的由来 一般来说,对于[1,n]的操作,修改与统计,复杂度是O(n),但是用先单数优化,可以达到O(log(n)) 比如 题目一: 10000个正整数,编号1到10000,用A[1],A[2],A[10000]表示。 修改:无 统计:1.编号从L到R的所有数之和为多少? 其中1<= L <= R <= 10000. 方法一:对于统计L,R ,需要求下标从L到R的所有数的和,从L到R的所有下标记做[L..R],问题就是对A[L..R]进行求和。 这样求和,对于每个询问,需要将(R-L+1)个数相加。 方法二:更快的方法是求前缀和,令 S[0]=0, S[k]=A[1..k] ,那么,A[L..R]的和就等于S[R]-S[L-1], 这样,对于每个询问,就只需要做一次减法,大大提高效率。 题目二: 10000个正整数,编号从1到10000,用A[1],A[2],A[10000]表示。 修改:1.将第L个数增加C (1 <= L <= 10000) 统计:1.编号从L到R的所有数之和为多少? 其中1<= L <= R <= 10000. 方法一:直接修改第L个数 方法二:把L到R的所有值都要加上C 所以,从上面可以看出来,普通的方法对于修改比较快,但求和比较慢。前缀和求和比较快,但修改比较慢 所以,基于此问题,将修改和统计集与一体的线段树就诞生了 线段树介绍

Educational Codeforces Round 69 E - Culture Code (最短路计数+线段树优化建图)

核能气质少年 提交于 2019-11-28 14:51:31
题意:有n个空心物品,每个物品有外部体积 outi和内部体积 i n i ,如果 i n i > o u t j ,那么j就可以套在i里面。现在我们要选出n个物品的一个子集,这个子集内的k个物品全部套在一起,且剩下的物品都无法添加到这个子集中(没有空间塞进去)。 定义浪费的空间为子集中空心的部分,即 i n i 1 + ( i n i 2 − o u t i 1 ) + ( i n i 3 − o u t i 2 ) + ⋯ + ( i n i k − o u t i k − 1 ) ini1+(ini2−outi1)+(ini3−outi2)+⋯+(inik−outik−1)。求浪费空间最少的子集个数。 解法:第一时间能想到最短路计数,但是朴素建图办法是n^2的。不会线段树优化建图,这里学习的是 https://www.cnblogs.com/birchtree/p/11274812.html 这位大佬的。 上面大佬的博客说得十分好了。线段树优化的原理其实就是通过一棵线段树当作工具树,这棵树不附带信息,只是作为一个桥梁连原图结点,且因为线段树能极短地表示区间的优点使得: 向区间连边 时极大的优化边数。 从而达到优化边数的目的。 #include<bits/stdc++.h> using namespace std; const int N=1e6+10; const int