主席树

学习笔记(带修主席树)

大城市里の小女人 提交于 2020-03-12 07:31:52
带修主席树 感谢YMY大佬非常非常详细的口糊和debug(v.) ,首先主席树是离线算法。 普通主席树是权值线段树,求区间里有几个数,就是用前缀和相减的方式。 其实带修主席树也大同小异。 算法实现 首先你需要离线所有的操作,主要是要将修改之后的值也离散进取 对于每次修改,用树状数组的方式每次加lowbit(),对每个点都insert一下。 对于每次询问,先开两个数组,将左右端点树状数组的跳的点都存下来,每次查询区间都用所用又端点减所有左端点,进左右子树时,也要将这些点换成左右子树。 洛谷 Dynamic Ranking #include<bits/stdc++.h> using namespace std; typedef int sign; typedef long long ll; #define For(i,a,b) for(register sign i=(sign)a;i<=(sign)b;++i) #define Fordown(i,a,b) for(register sign i=(sign)a;i>=(sign)b;--i) const int N=1e4+5; bool cmax(sign &a,sign b){return (a<b)?a=b,1:0;} bool cmin(sign &a,sign b){return (a>b)?a=b,1:0;}

主席树

吃可爱长大的小学妹 提交于 2020-03-12 07:16:57
问题 给出n个数,q个询问,求l-r内的第k小值(n,q<=2e5) 方法一:平衡树 方法二:主席树 下面来看一看主席树是怎么做的。 主席树是一种特殊的线段树,针对这一题,我们可以对每个区间[x,y]维护一颗线段树,[l,r]表示在[x,y]区间内,数的大小在[l,r]范围内的数的个数。 但这种思想的实现一定超过了空间与时间的限制。 考虑优化时间。考虑前缀和的思想,首先离散化数据,sum[1,i]-sum[1,j-1]即代表了区间sum[j,i],这里不妨也可以运用这种思想,只对每一个前缀进行维护。 但光有上述这点还是不够的。我们会发现,对于sum[1,i]和sum[1,i+1]这两个状态,只有一个元素的差异,所以可以考虑持久化。 即对sum[1,i+1]的[l,r]来说,若[l,mid]和sum[1,i]是相同的,则可以直接指向sum[1,i]的[l,mid],若是不相同的,则新建一个节点。很容易发现,这样的空间复杂度是logn*n的。 补充1:对于2,有其一定的实现技巧。 //此处的insert指插入x节点 procedure insert(pre,x,h,t:longint); var tmp,mid:longint; begin inc(now); p[now]:=p[pre]; inc(p[now].x); tmp:=now; if h=t then exit; mid:

主席树

天大地大妈咪最大 提交于 2020-03-02 00:44:39
主席树是什么 先思考这样一个问题,给定一段序列,和若干个询问,每次查询区间[L,R]中第k小的数。 挂上模板题: 洛谷3834 现在来想一下具体做法,首先我们知道求整个序列的第k大可以用权值线段树( 假设已经会了 ),那么我们想如何求[L,R]的呢,我们想如果已经知道了[1,L]和[1,R]这两棵权值线段树是不是就能够得到答案了呢(当然是了),我们还需要用求全局第k小的方法在向下递归的过程中不断从[1,R]中把[1,L]摘出来即可。 主席树的具体实现 用了上面方法我们知道了解决这个问题的方法,但是这样的操作需要开n棵权值线段树,内存肯定是吃不消的,那么有什么方法可以减少内存开支呢,我们发现在新添加一个元素的时候实际上只修改了某一条链,如果我们只是把链存起来的话,那么空间开支就会变成nlogn。具体的实现方法就是说在每次新插入一个值的时候,新开一个根,将所有新修改的元素组成的这条链和原来已有的树相连,每个节点只保存左右儿子节点而不存父亲节点。如下图所示: 我之前所看到的大多数博客都采用了数组的写法,这里我使用的是指针的写法具体插入某一个值的操作为: void Insert ( LE & k , LE kk , int val , int L , int R ) { k = new Node ; k - > val = 0 ; k - > f = kk - > f ; k - > t

主席树--动态区间第k小

早过忘川 提交于 2020-02-28 06:49:42
主席树--动态区间第 \(k\) 小 模板题在这里 洛谷2617 。 先对几个问题做一个总结: 阅读本文需要有主席树的基础,也就是通过区间kth的模板题。 静态整体kth: sort一下找第k小,时间复杂度 \(O(nlogn)\) 。 动态整体kth: 权值线段树维护一下,时间复杂度 \(O(nlogn)\) 。 静态区间kth: 主席树维护,时间复杂度 \(O(nlogn)\) 。 动态区间kth: 就是本次的标题。 回忆一下主席树是如何维护静态区间kth的。 建立可持久化线段树后,利用前缀和的思想查询区间的kth。 所以我们想对区间kth带修改操作,前缀和是关键。 我们在维护普通前缀和,支持查询和修改操作时,用的是什么数据结构呢? 树状数组/线段树。 所以这时候我们大致有一个概念了,动态主席树和主席树在 数据结构 上已经多少有点不一样了。 动态主席树:树套树 。 静态主席树:可持久化权值线段树 。 怎么套是一个问题,但简单想想可以发现,我们在 外层维护一颗树状数组,树状数组的每个节点(内层)维护权值线段树(的根节点) ,可以解决这个问题。 修操作: 如果将一个位置的数字 \(a_i=x\) 修改为 \(y\) ,那么在外层树状数组上,我们需要修改 \(logn\) 个节点,同时对于每个节点(代表了一颗权值线段树),分别有 \(logn\) 个节点受影响,所以修改复杂度为 \

算法笔记:带修主席树(树套树)

試著忘記壹切 提交于 2020-02-28 04:48:00
从我短暂的ACMer生涯当中学到一件事———越是玩弄数据结构,就越会发现树的能力是有极限的… 除非超越树。 那就再套一层树吧!Wryyyyy!!! 最近打算研究一波树套树,平衡树怎么套还没搞懂,目前学习了一点套主席树的方法,平衡树的内容可能也会补在这吧。 众所周知,主席树维护的是一种类似前缀和的结构,每个节点都是包含了之前所有节点值的权值线段树,通过继承上一个节点权值线段树的部分结构以减少大量的空间和时间。 因为维护的是前缀和的结构,因此主席树满足可减性,在解决如静态区间第k小等问题中只需要取区间右端的树减区间左端的树即可得到仅包含有区间内值的权值线段树,这其实就类似于求一个序列的某个区间和可以用前缀和数组,区间右端的值减区间左端的值得到。 当然,以上都是废话,会写主席树的话肯定也知道这些东西。不过我还是要写出来,是因为想展示主席树其实本身也是一种“数据结构套数据结构”的形式,把每个节点的权值线段树抽象成点,主席树的上层就是一个简单的前缀和数组,下层使用权值线段树代替了前缀和数组中的每一个位置。而带修主席树不外乎就是把这个上层结构更换了一下,换成树状数组或线段树之类的其它数据结构。 以下通过两个简单的板子题展示下如何使用树状数组套主席树( 其实应该是树状数组套权值线段树 洛谷 P2617 Dynamic Rankings 题意: 给一个含有n个数的序列,需要支持两种操作: 1

HDU-6278 Just h-index (主席树+二分)

放肆的年华 提交于 2020-02-17 15:17:59
题目描述 The h-index of an author is the largest h h h where he has at least h h h papers with citations not less than h h h . Bobo has published n papers with citations a 1 , a 2 , … , a n a_1,a_2,…,a_n a 1 ​ , a 2 ​ , … , a n ​ respectively. One day, he raises q questions. The i-th question is described by two integers li and ri, asking the h-index of Bobo if has only published papers with citations a l i , a l i + 1 , … , a r i a_{l_i},a_{l_{i+1}},…,a_{r_i} a l i ​ ​ , a l i + 1 ​ ​ , … , a r i ​ ​ . The input consists of several test cases and is terminated by end-of-file. The first line of

[SDOI2013]森林(树上主席树)

 ̄綄美尐妖づ 提交于 2020-02-07 10:45:21
[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值。初始的时候,森林中有M条边。 小Z希望执行T个操作,操作有两类: Q x y k 查询点x到点y路径上所有的权值中,第k小的权值是多少。此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点。 L x y 在点x和点y之间连接一条边。保证完成此操作后,仍然是一片森林。 为了体现程序的在线性,我们把输入数据进行了加密。设lastans为程序上一次输出的结果,初始的时候lastans为0。 对于一个输入的操作 Q x y k ,其真实操作为 Q x^lastans y^lastans k^lastans 。 对于一个输入的操作 L x y ,其真实操作为 L x^lastans y^lastans 。其中^运算符表示异或,等价于pascal中的xor运算符。 请写一个程序來帮助小Z完成这些操作。 对于所有的数据,n,m,T<= 8*10^4 8 ∗ 1 0 4. 输入格式 第一行包含一个正整数testcase,表示当前测试数据的测试点编号。保证1<=testcase<=20。 第二行包含三个整数N,M,T,分别表示节点数、初始边数、操作数。 第三行包含N个非负整数表示 N个节点上的权值。 接下来 M行,每行包含两个整数x和 y,表示初始的时候

hdu6621--主席树

孤街醉人 提交于 2020-01-31 19:14:13
K-th Closest Distance 题意:给定长度为 \(n\) 的数列,现有m次查询,每组询问给 \(l,r,p,k\) ,问对 \(l<=i<=r\) , \(|p-a[i]|\) 的第k小值。数据强制在线。 题解:二分答案ans,用主席树查询 \(a[l]\) 到 \(a[r]\) 之间 \([p-ans,p+ans]\) 的个数,个数为k即为答案。 ​ 由于强制在线,所以建主席树不能离散化。可以动态空间直接建树。 #include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,m; const int N=1e6; struct node{ int ls,rs,sum; }; node tree[maxn*22]; int rt[maxn],tot=0; int update(int l,int r,int x,int pre){ int now=++tot; tree[now]=tree[pre]; if(l==r){ tree[now].sum++; return now; } int mid=(l+r)>>1; if(x>mid) tree[now].rs=update(mid+1,r,x,tree[pre].rs); else tree[now].ls=update(l

P3402 【模板】可持久化并查集(可持久化并查集模板)

喜夏-厌秋 提交于 2020-01-25 14:00:49
题目链接: 模板题 普通的并查集是通过 f a fa f a 数组来实现的(或者说是 p p p 数组) 可持久化并查集,就是用主席树来维护并查集的 f a fa f a 数组(因为主席树可以记录过去的版本),使之能回退到过去的版本,这里的版本指的是某个操作之前的 f a fa f a 数组 可持久化并查集的合并部分使用的不是路径压缩来优化,而是使用启发式合并:两棵树合并时,将树深小的合并到树深大的,这样树的高度增长比较慢。 若合并两棵树,新的树的高度会增长,那么树的 s i z e size s i z e 至少扩大两倍,因此树高至多增长 log ⁡ n \log n lo g n 次,每次增长的值为 1。 主席树部分: 先建一棵初始的主席树,令每个下标 i i i 的 f a [ i ] = i fa[i] = i f a [ i ] = i void build ( int & rt , int l , int r ) { rt = ++ sz ; if ( l == r ) { dep [ rt ] = 1 ; fa [ rt ] = l ; return ; } int mid = l + r >> 1 ; build ( lson [ rt ] , l , mid ) ; build ( rson [ rt ] , mid + 1 , r ) ; } 更新

对主席树的理解以及使用

為{幸葍}努か 提交于 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