各种数据结构的知识点归纳

各种数据结构的知识点归纳

By SemiWaker


各种莫队

普通的莫队

设x所在块的编号为BID[x]。
把所有询问[L,R]按照(BID[L],R)的二元组去排序。
从第一个询问开始,每次暴力移动询问区间。
暴力移动的过程中,将L和R每次移动一个,加入或者删除区间的端点。
因为每次只会加入或者删除一个,而且空间只需要维护一个区间的信息,所以可以完成大多数区间询问。
当信息不满足结合律时,不能用树形结构,此时莫队算法就有用了。
分块大小为 n ,总时间复杂度 O(nn)

小优化:当下一个询问的R比当前询问的R小,说明已经遍历了一次了。这时不需要一个一个地将R往左移动,直接从下一个询问开始即可。

带修莫队

考虑加入修改信息。
将询问设为[T,L,R],T是第T次修改后的意思。
那么按照(BID[T],BID[L],R)的三元组进行排序,重复莫队的过程即可。
考虑修改,因为我们会加入和取消某一次修改,所以每一次修改都要记录修改前的和修改后的信息。
如果修改在[L,R]之间,就要计算贡献。
如果修改不在[L,R]之间,直接在外面修改。
块的大小为 n23 ,总时间复杂度 O(n53) 。是很极限的,易卡。

树上莫队

区间的分块的方式为树上分块,通常按高度或者DFS序分块即可。
修改次数按照普通的方法即可。
排序时,R要按照DFS序排序。
移动区间的方式和在序列上的是有区别的。
我们给在询问内的节点打上标记,以判断每一个节点在不在询问内。
这样,在树上移动就相当于给每个节点取反,在询问内的就取出,不在的就加入。
设原来的询问为x、y,后来的询问为x1,y1。
将x到x1的路径取反,y到y1的路径取反。
取反的过程中,不取反LCA。
这时,再将x和y的LCA取反,以及x1和y1的LCA取反。
求LCA的过程在莫队之外,所以不增加时间复杂度。

只有插入的莫队

对于大部分题目来说,只有插入都会比有插入和删除要简单。
普通的莫队是有删除的,当左端移动的时候,就会发生删除。
但是,是有办法不用删除的。
考虑到:左端只在一个 n 的块里移动,如果我们把询问分成两个部分:
左端所在块内 , 左端所在块后到右端。
那么,我们就可以是两部分都只有插入。
左端所在块大小为 n ,我们暴力预处理后缀和即可。
右端本来就只有插入。
这样,莫队算法就可以只维护插入了。


各种普通的离线算法

对L计算贡献

将询问按R排序。
从左到右扫描。
对于每一个 Ai,考虑询问的 L 在哪些位置时,Ai 对该询问有贡献。
修改 Ai 有贡献的 L 位置的区间。
对于每一个 R 在当前位置的,询问 [L,R] 区间。

历史最优

在对L计算贡献的基础上,如果询问为:[L,R] 区间中的最优子区间,那么记录历史最优值。
即:当 R 移动时,可能会改变最优值。
那么 R 在 [L,R] 之间时的历史最优值,就是 [L,R] 区间中最优的子区间。

对R计算贡献

同对 L 计算贡献,但是每次是删除 AL,如果删除比加入好做的话可按L排序。

取消影响

对 R 排序,每次取消掉 [1,L-1] 的影响,余下的就是 [L,R] 。


有关颜色的问题

颜色:区分不同种类的信息
例如:区间颜色数,区间只出现一次的颜色数,区间出现多次的颜色数etc。

  1. 暴力莫队,用一个数组记下每个颜色出现次数。易想出,但是是最坏的选择。
  2. 排序询问,扫描。对于每一个点x,记录上一个和它颜色相同的点的位置Pre[x]。每扫描到一个点的时候,它的贡献区间会变化。用树状数组或者线段树去维护贡献区间。对于每一个询问,在R的位置或者L的位置查询。
  3. 分块。如果有修改的话,通常不好排序询问。这时用分块维护每一块的颜色信息。考虑有贡献的x的Pre[x]需要满足的条件,用分块维护Pre[x]。
    Pre[x]有暴力的维护法,和不暴力的维护法。不暴力的还要每一块维护每个颜色在当前块之前的第一个,和在当前块之后的第一个。修改一个点的颜色时,新的颜色的前一个和后一个可能在块内,也可能在块外,块内暴力,块外直接取。如果新的颜色是当前块最后一个或者第一个,还要修改其他块的该颜色的信息。
  4. 树套树。通常不如分块。
  5. 每种颜色建线段树。注意到:所有颜色的点数加起来总共为n。如果我们的线段树不完全建全,只建有点的部分,那么总空间一共O(nlogn),是可以接受的。通常用来处理单颜色的询问。

通用线段树、平衡树

当操作和记录的信息差别很大的时候,怎样才可以不打两次线段树(平衡树)?

先看每一个点实际要记录什么,实际上除了树结构本身外,只有标记和数据两个部分。
标记很少不下传的,所以默认标记要下传。
操作方式默认为基本段改+段查询,简化为打标记+求一段的数据。
(翻转不考虑)

再考虑标记和数据分别会发生什么?
标记:打上标记时初始化,下传时将两个标记合并,将标记应用到数据上。
数据:初始化,两个数据合并,应用标记。

其他东西都是一样的。

然后我们就可以把标记和数据封装一下。
初始化写入构造函数,合并重定义一下+号,应用标记给数据写一个Apply函数。

要求数据和标记都是可以合并的,而且满足结合律。

然后就可以把线段树(平衡树)写成模板了。

注意点:
1、默认数据不满足交换律,没有单位元,所以询问要写成三段式。
2、平衡树的数据合并方式是:Data[lef]+Data[self]+Data[rig],所以每一个点实际要保存两个数据:自己和自己表示的一段。
3、翻转(将第一个和最后一个交换,第二个和倒数第二个交换……)另外写。
4、数据和标记是对应的。


区间和单点的转换

  • 区间询问转单点询问:
    把区间询问变为前缀和,此时每个区间询问变为单点询问。
  • 区间修改转单点修改:
    配合上面的前缀和,我们可以在区间左端点+,右端点之后-。
    这时,当前修改只对在修改区间内的询问有贡献。
  • 路径询问转单点询问+区间修改
    先做DFS序,这时子树变成区间。
    每个点x维护到根的和Sx。
    那么我们可以把路径和变为 Sx+Sy2SLCA+ALCA
    如果是边权,路径和变为 Sx+SySLCA
    修改一个点的时候,变为修改整棵子树。
    特殊情况:异或时,点权和: Sx XOR Sy XOR ALCA
    边权和 Sx XOR Sy ,这种情况下只和x和y有关。

用于匹配的数据结构

首先,要把匹配的内容给Hash了,才能放进数据结构里。

  • 全局匹配,直接开Map或者开个权值线段树,权值线段树最好是不开完全的,这样可以保持值域比较大。
  • 区间匹配。主席树,右端点减去左端点之后,再判断是否存在。
  • 树上匹配。沿着树建主席树即可。

简单来说,重点还是Hash。


By SemiWaker

你可能感兴趣的:(OI,数据结构)