7.9&7.11 DS

时间:2021-07-16
本文章向大家介绍7.9&7.11 DS,主要包括7.9&7.11 DS使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
\[\begin{aligned}\begin{gathered}\huge\cal{D}\textsf{ata}\ \huge\cal{S}\textsf{tructures}\ \large\rm{-ZH\_comld} \end{gathered}\end{aligned} \]
  • 数据结构要形成自己的写法。

树状数组

一维树状数组

一些注意事项:

  • 下标从 \(1\) 开始

  • \(\operatorname{lowbit}(x)=x\&-x\)

二维树状数组

  • 区间修改 / 查询时对两维分别枚举 \(\operatorname{lowbit}\)

树状数组上二分

  • 与线段树二分类似,判断当前状态对最终答案的贡献,根据大小进行二分。

线段树

线段树合并

  • 把很多权值线段树合并起来

两种做法:

  • 对询问合理排序,合并时把一棵树合并到另一棵树上并使得询问都能得到答案 (空间小) 。
  • 每次合并新开节点,不需要离线 (空间大) 。

可持久化线段树 (主席树)

  • 静态区间第 \(k\)
  • 区间 \(mex\) (没有出现的最小自然数)

动态开点,每次单点操作最多只对 \(\lceil\log n\rceil\) 个节点有影响,于是新建 \(\lceil\log n\rceil\) 个节点进行维护。

时间复杂度:\(O(n\log n)\)

典例:

扫描线

  • 求矩形面积并 / 周长并

面积并:先把所有横向边对 \(y\) 坐标排序,再把所有端点在 \(x\) 轴上进行离散化,对中间的所有线段开一棵线段树维护该线段内被整个图形所截的长度,扫描时进行维护。

周长并:

树链的并

  • \(n\) 个点的树,给定 \(k\) 个点,求它们到根的边权并。

先按 DFS 序排序,答案即为:

\[\sum_{i=1}^k dis_{k_i,root}-\sum_{i=1}^{k-1} dis_{lca_{k_i,k_{i+1}},root} \]

虚树

  • 给定一棵 \(n\) 个节点的树 \(T\),构造一棵新的树 \(T'\) 使其包含指定的 \(k\) 个节点和他们的 LCA,并使总节点数最小。

树链剖分

  • 一个会让你代码长度增加 \(1kb\) 的数据结构。

分块

  • 是一种暴力的优化。

对长度为 \(n\) 的序列分为 \(\sqrt n\) 个长度为 \(\sqrt n\) 的块,每一次操作只需分为整块的操作和单点的操作。

时间复杂度:\(O(n\sqrt n)\)

莫队

  • 是一种基于分块的算法。

朴素莫队

  • \(k\) 次询问,每次询问 \([l,r]\) 内的颜色个数。

适用范围:从 \([l,r]\)\([l+1,r]\) 的转换比较快捷。

先对序列分块,再对所有询问按 \(l,r\) 排序,从一个询问到下一个询问只需要暴力拓展边界即可。

时间复杂度:\(O(n\sqrt n)\)

带修莫队

  • 同上,但要支持单点修改。

对于修改的操作,实际上只是增加了一个时间轴,此时对于每次询问按 \(l,r,t\) 排序求解。

注意分块大小为 \(n^{\frac{2}{3}}\),总时间复杂度:\(O(n^{\frac{5}{3}})\)

回滚莫队

适用条件:区间转移时无法实现增加 / 删除。

笛卡尔树

  • 一种特定的二叉数据结构。
  • 具有堆的性质,满足父节点比儿子节点大。
  • 这棵树的中序遍历就是原来的序列。

建树做法:单调栈维护右链,时间复杂度 \(O(n)\)

for(int i=1;i<=n;i++){
    while(top&&a[st[top]]>=a[i])
        	ch[i][0]=st[top--];
   	if(top) ch[st[top][1]]=i;
    st[++top]=i;
}

基环树

  • \(n\) 个点,\(n\) 条边的连通图,环上的每个点ac都是其子树的树根。
  • 对于有向图上的基环树,分为内向基环树和外向基环树。

二叉查找树 (Binary Search Tree)

  • 中序遍历等同于原序列。
  • 可以在某一位置插入,在 BST 上查找前驱后继。

Treap

  • 堆和 BST 结合的平衡树。
  • 旋转操作:在不改变原查找树的中序遍历下将一个节点向上翻转。

插入:在 BST 上寻找应该插入的位置 (类比线段树上二分) 。

删除:将所要删除的节点旋转到叶子再删除。

右旋:先将根的左儿子的右儿子连向根,再将根连向根的左儿子,最后重新维护所改变的节点。

左旋:先将根的右儿子的左儿子连向根,再将根连向根的右儿子,最后重新维护所改变的节点。

Splay

  • 每次对一个点操作完成后要把这个点旋转到根节点的位置来保证复杂度。

插入:与 Treap 类似,找到合适的点插入后将其旋转到根。

删除:将根的左儿子的右儿子旋转到左边,再将根的右儿子连到根的左儿子右边,最后删除根。

双旋:每次用双旋将一个节点旋转到根的位置。

FHQ Treap (无旋 Treap)

  • BST + Treap

最方便的平衡树 (STL)

std::set  std::map

点分治

  • 主要用来求解树上路径问题。
  • 树上路径总数很多,但如果确定一个分治中心,然后将其子树内的路径合并就会变得很快。
  • 怎么选分治中心 —— 就是树的重心。
  • 选择树的重心后,分治下去的子树大小至少会变成原来的一半,所以最多会分治 \(\log n\) 层,总时间复杂度 \(O(n\log n)\)

边分治

contact me on QQ (601585974 布鲁)

原文地址:https://www.cnblogs.com/blueqwq/p/15019165.html