zr数据结构

线段树合并:

Done.

Done.(CF600E)

首先一般维护值域线段树,复杂度是 \(O(k log k)\),k是插入点个数。

可以用于解决每个节点用线段树维护信息,然后所有点构成树形结构,需要查询子树信息的一类问题。

这种东西很自然的能想到用线段树合并。具体方法是对于两棵树从根开始递归,若左/右儿子只有一个就赋成有的那个,若有两个就递归到他们子树过去合并,然后合并完子树的时候把两个节点的信息顺便合并一下。

有两种写法。一种是直接用原树上的节点,一种是类似主席树新开一个节点存信息,后者缺点是空间非常爆炸,前者缺点是询问后一般不能再修改,会破坏原来的信息。

线段树分裂:

Done.

(实际上整合了上下总共三个线段树XX)

这个

将一棵(权值)线段树按小于等于k和大于k分成两棵。直接递归处理即可。可以用取地址符简化代码。

线段树二分:

直接看左区间的信息是否满足要求,是就递归过去,否就递归右区间查询,递归右区间过程中注意统计答案。

顺带提一嘴树状数组二分:从0开始,从高到低位看要不要加 \(2^i\) 即可,因为树状数组维护 \(i-lowbit(i) + 1-i\) 的信息。

显然后者的常数远远小于前者,在都实现的比较优秀时,能差出至少 5 倍。

例:P6619,只能树状数组二分或zkw线段树二分,因为原题不开O2。做法是二分两个位置,一个是火系战士恰好小于等于冰系战士能量,一种是恰好多于。这两个可以分别维护前后缀树状数组二分。

不过一般3e5左右的数据线段树二分肯定很稳,1e6开个O2只要不写丑也可以线段树二分。

DSU on TREE:

这个

和线段树合并解决的问题类似。一般只能解决中间没有修改,只有询问子树信息的问题。当然你也可以转化成这个样子再上。

流程:

  • 树剖,遍历树,对于每个节点

  • 先递归轻儿子并计算信息

  • 再递归重儿子并计算信息

  • 将自己的信息更新

  • 先计算答案,然后如果自己是父亲的轻儿子,就把自己的更新撤销了

  • 通过这样,递归过程中就会撤销掉所有轻儿子的信息,并计算所有轻儿子的答案

复杂度证明就感性理解吧,若默认更新一次信息是 \(O(X)\) 的,则复杂度为 \(O(X n \log n )\) 的。可能因为和启发式合并很像,所以叫这么个鬼名字。

平衡树:

记过了

替罪羊树:

思想就是设定常数 \(\alpha \in (0.5,1.0)\),满足二叉树一个节点的左右儿子占总子树的大小比例不超过 \(\alpha\),若超过就暴力重构。容易发现选择 \(0.5\) 时即为完全二叉树,\(1.0\) 时为普通二叉搜索树。

一般的平衡树都能保持在 \(\alpha \in (0.5,0.8)\) 之内,且大多数为 \([0.6,0.7]\)

一般选择 \(\alpha \in [0.7,0.8]\) 作为比例。\(\alpha\) 越大插入越快查询越慢,反之亦然。在询问插入同数量级时一般选取 \(\alpha=0.75\)

重构方法就是中序遍历原树形成一个数组,然后用二分暴力建出新树。

删除直接暴力将对应节点的计数器减一,若删除过多,树中的点数不足一半,也需暴力重构。

李超树:

维护(平面直角坐标系的)线段的线段树。怎么表达这么鬼畜

用于做一类给定 \(n\) 条线,求 \(x=x_0\) 时的 \(y\) 最大值。

值域线段树上维护区间的优势线段,即在这段区间最上方的位置最多的线段。

支持修改,查询。当然首先要会求直线交点。

修改:

  • 区间无线段,则直接更新然后return。若在区间内无交点且新线段在上,则直接更新,否则直接return。

  • 区间有线段且有交点。求一下新线段和原始优势线段的交点,若交点在区间左半,那么若新线段斜率大就更新一下优势线段然后递归左区间,小则直接递归左区间。若在区间右半,则新线段斜率小就更新优势线段递归右区间,否则直接递归左区间。

查询:线段树跑到底,对于每一条线段都查询并取最值。

点分治/点分树

这个

点分治:找到树上一个点使得以其为根,其最大子树最小,然后以它作为分治点将他删去得到若干连通块继续分治统计答案。每次断开之前可以先计算这部分各个子树互相的答案。

点分树:将上一次断开的点设为这一次断开的点的父亲构成一棵新树,此之谓点分树。它满足所有节点在新树上的深度之和是 \(O(n \log n)\)。因此可以通过暴力+其他东西统计很多奇奇怪怪的信息。

一般要套亿些数据结构支持查询。

KDT

比较简单,直接粘一段:

在查询矩形区域内的所有点的权值和时,仍然需要记录子树内每一维度上的坐标的最大值和最小值。如果当前子树对应的矩形与所求矩形没有交点,则不继续搜索其子树;如果当前子树对应的矩形完全包含在所求矩形内,返回当前子树内所有点的权值和;否则,判断当前点是否在所求矩形内,更新答案并递归在左右子树中查找答案。

还剩下segment tree beat没整完,有时间整一下

题目:

P2824:加强:数据范围5e5,多次询问。

做法:类似于ODT的做法维护区间,然后每个区间维护是单增还是单减,排序就相当于ODT的合并区间,但是可以用线段树维护一下,然后合并和分裂只需要写线段树合并和分裂即可。虽然巨大难写但是是略大常数 \(O(n \log n)\) 的。

CF765F:

Done.

题意:查询区间最小差,即 \(\min_{l\le i<j\le l}(|a_i-a_j|)\)。询问3e5,n 1e5。

于是对询问离线排序,扫描线一下,然后维护一下 \(ans_i\) 表示 \(i-r\) 的答案,每次新加入一个节点就线段树上更新一下答案。

先将线段树上每个区间存入区间内的数,然后建树时将其排序。

我们可以将原询问变成询问 \(\min_{i\in [l,r]}(ans_i)\)

我们先对于线段树中右区间算答案,同时维护当前更新出来的最优答案。然后对于一个区间,我们先用区间中最优的两个数更新一下该区间的答案,然后如果发现最新的答案仍然大于等于目前维护的答案,那么显然不可能使答案优,直接return掉即可(因为我们转化了询问,查询的时候显然可以用右边的覆盖掉这个位置)。

于是可以直接维护了。

复杂度呢?

设有三元对 \((i,j,k)\) 满足 \(i<j<k\),则 \(k\) 能更新答案当且仅当 \(a_k-a_i < a_j-a_k\),那么两边同时加上 \(a_k-a_i\),得到 \(a_k-a_i < (a_j-a_i)/2\),即答案每次至多是之前一半。因此,我们可以轻松得出复杂度是 \(O(n \log^2 V)\) 的。

P7219:

题意:$n\times n $ 的图片,对于第 \(i\) 列有一个 \(a_i\) 高度的房子,有 \(m\) 个位置 \((x_i,y_i)\),删除一个位置的代价是 \(C_i\),要求最小化删掉点的价值,满足没有任何两个剩余的给定点在同一个不含楼房的小矩形中(称之为不构成星座)。即求最大保留代价。

发现两个点构成星座当且仅当 \(x_i-x_j\) 中的最大楼房高度小于 \(\min(y_i,y_j)\)。于是想到上笛卡尔树。

建出大根笛卡尔树后,我们设 \(f_{i,j}\) 表示 \(i\) 子树中高度最大的星星为 \(j\) 的最大保留代价。

那么设 \(h\) 为当前节点子树的最大楼房高度,\(maxx=\max_{i=1}^h(f_{chx,i})\)\(maxy\) 同理,则有如下转移:

\(f_{now,h}=maxx+maxy\)\(f_{now,i}=\max(maxx+f_{chy,i},maxy+f_{chx,i}),i\in (h,n]\)

发现这是一个线段树合并的形式,因为要支持区间求max,区间加以及合并。具体说来,我们对当前节点的左右儿子算完答案之后,将其 \(maxx,maxy\) 求出来,然后对于两棵子树大力合并,最后对于 \(f_{now,h}\) 这个位置修改一下即可。

CF1137F:

题意:给定一棵树,每次删去编号最小的叶子节点,有两种操作,求一个节点是第几个被删的,以及将一个节点的编号设为max。

考虑将最大编号设成根,则最大编号到次大编号上所有点显然是从次大开始到最大,最后几个被删的。

那么对于将节点编号变成max,该点到原先最大点的路径上的点都是最后删的,很像LCT的access操作,而新的最大点又需要换根,这又很像LCT的makeroot操作。综合起来,就是一个makeroot操作。

于是可以维护LCT,修改操作如上。对于一棵splay,我们给他赋一个独特的col标记(大小也代表删的顺序),然后对于询问第几个被删的,需要维护一个树状数组查询所有在某棵splay之前的点数,则答案为 \(query(splay_x)+size\),其中query表示树状数组的查询,而size表示这棵splay中在他之前删的点数。

注意修改时对于splay的更新,我们需要将旧的断开的splay减少的size从树状数组中减掉,并加到新的splay对应的col上。

最后注意维护一下每棵splay的col即可。

P3721:

维护单旋spaly。

对于add操作,因为前驱后继一定在树上连续,所以将其直接塞到深的那个的儿子即可。

对于单旋max/min,就是将那个节点取出来,将其原来的儿子挂到父亲上,将整棵树挂到他下面。这时它的子树以外的点的深度都+1。

对于删除max/min,就是上一种操作不把树挂到他下面了。这时只有他子树的深度-1。

那么维护权值线段树(key值),然后对于修改直接做(因为BST满足的性质保证子树必然是连续区间,可以直接线段树操作),并且还要通过线段树二分找到前驱后继,然后顺带维护下原树的结构即可。当然维护结构就是模拟的活了。

UOJ515,UOJ291:这两个先寄了,有时间再说吧。

posted @ 2022-08-26 15:33  infinities  阅读(58)  评论(0编辑  收藏  举报