From af8b3eabae2bf6ae5a716495a6b31c3d8de44bbb Mon Sep 17 00:00:00 2001 From: 24OI-bot <15963390+24OI-bot@users.noreply.github.com> Date: Wed, 21 Nov 2018 14:40:14 +0800 Subject: [PATCH] style: format markdown files with remark-lint --- docs/ds/persistent-balanced.md | 40 ++++++++++++------------ docs/ds/sparse-table.md | 69 ++++++++++++++++++++---------------------- 2 files changed, 53 insertions(+), 56 deletions(-) diff --git a/docs/ds/persistent-balanced.md b/docs/ds/persistent-balanced.md index c355845b..40f944ea 100644 --- a/docs/ds/persistent-balanced.md +++ b/docs/ds/persistent-balanced.md @@ -32,24 +32,24 @@ Split-Merge Treap 在复制一个节点 $X_{a}$($X$ 节点的第 $a$ 个版本) 的新版本 $X_{a+1}$ ($X$ 节点的第 $a+1$ 个版本) 以后: -- 如果某个儿子节点 $Y$ 不用修改信息,那么就把 $X_{a+1}$ 的指针直接指向 $Y_{a}$ ($Y$ 节点的第 $a$ 个版本) 即可。 -- 反之,如果要修改 $Y$ ,那么就在**递归到下层**时**新建** $Y_{a+1}$ ($Y$ 节点的第 $a+1$ 个版本) 这个新节点用于**存储新的信息**,同时把 $X_{a+1}$ 的指针指向 $Y_{a+1}$ ($Y$ 节点的第 $a+1$ 个版本)。 +- 如果某个儿子节点 $Y$ 不用修改信息,那么就把 $X_{a+1}$ 的指针直接指向 $Y_{a}$ ($Y$ 节点的第 $a$ 个版本) 即可。 +- 反之,如果要修改 $Y$ ,那么就在**递归到下层**时**新建** $Y_{a+1}$ ($Y$ 节点的第 $a+1$ 个版本) 这个新节点用于**存储新的信息**,同时把 $X_{a+1}$ 的指针指向 $Y_{a+1}$ ($Y$ 节点的第 $a+1$ 个版本)。 ### 可持久化 需要的东西: -- 一个 struct 数组 存**每个节点**的信息 (一般叫做 tree 数组); (当然写**指针版**平衡树的大佬就可以考虑不用这个数组了) +- 一个 struct 数组 存**每个节点**的信息 (一般叫做 tree 数组); (当然写**指针版**平衡树的大佬就可以考虑不用这个数组了) -- 一个**根节点数组**,存每个版本的_树根_,每次查询版本信息时就从**根数组存的节点**开始; +- 一个**根节点数组**,存每个版本的_树根_,每次查询版本信息时就从**根数组存的节点**开始; -- split() 分裂 **从树中分裂出两棵树** +- split() 分裂 **从树中分裂出两棵树** -- merge() 合并 **把两棵树按照随机权值合并** +- merge() 合并 **把两棵树按照随机权值合并** -- newNode() 新建一个节点 +- newNode() 新建一个节点 -- build() 建树 +- build() 建树 #### Split @@ -59,8 +59,8 @@ std::pair <int,int> split(x,k) 返回一个 std::pair; 表示把 $_x$ 为根的树的前 $k$ 个元素放在**一棵树**中,剩下的节点构成在另一棵树中,返回这两棵树的根(first 是第一棵树的根,second 是第二棵树的)。 -- 如果 $x$ 的**左子树**的 $key ≥ k$,那么**直接递归进左子树**,把左子树分出来的第二颗树和当前的 x **右子树**合并。 -- 否则递归**右子树**。 +- 如果 $x$ 的**左子树**的 $key ≥ k$,那么**直接递归进左子树**,把左子树分出来的第二颗树和当前的 x **右子树**合并。 +- 否则递归**右子树**。 ```c++ static std::pair _split(int _x, int k) { @@ -121,17 +121,17 @@ static int _merge(int _x, int _y) { 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本): -1. 插入 x 数 +1. 插入 x 数 -2. 删除 x 数(若有多个相同的数,因只删除一个,如果没有请忽略该操作) +2. 删除 x 数(若有多个相同的数,因只删除一个,如果没有请忽略该操作) -3. 查询 x 数的排名(排名定义为比当前数小的数的个数 + 1。若有多个相同的数,因输出最小的排名) +3. 查询 x 数的排名(排名定义为比当前数小的数的个数 + 1。若有多个相同的数,因输出最小的排名) -4. 查询排名为 x 的数 +4. 查询排名为 x 的数 -5. 求 x 的前驱(前驱定义为小于 x,且最大的数,如不存在输出 -2147483647) +5. 求 x 的前驱(前驱定义为小于 x,且最大的数,如不存在输出 -2147483647) -6. 求 x 的后继(后继定义为大于 x,且最小的数,如不存在输出 2147483647) +6. 求 x 的后继(后继定义为大于 x,且最小的数,如不存在输出 2147483647) 和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 3, 4, 5, 6 即保持原版本无变化)。 @@ -153,12 +153,12 @@ static int _merge(int _x, int _y) { ### 推荐的练手题 -1. luogu P3919 可持久化数组 (模板题) +1. luogu P3919 可持久化数组 (模板题) -2. codeforces 702F T-shirt +2. codeforces 702F T-shirt ### 另外 -1. 可持久化平衡树可以用来维护动态凸包,仙人掌等东西,如果读者有兴趣可以阅读相应的 [**计算几何**](/geometry) 知识,再来食用。 +1. 可持久化平衡树可以用来维护动态凸包,仙人掌等东西,如果读者有兴趣可以阅读相应的 [**计算几何**](/geometry) 知识,再来食用。 -2. Zip Tree 作为一种新的数据结构在 2018.8 月由 Robert E. Tarjan - Caleb C. Levy - Stephen Timmel 提出,可以去了解一下~ +2. Zip Tree 作为一种新的数据结构在 2018.8 月由 Robert E. Tarjan - Caleb C. Levy - Stephen Timmel 提出,可以去了解一下~ diff --git a/docs/ds/sparse-table.md b/docs/ds/sparse-table.md index 620b6abc..56d93c27 100644 --- a/docs/ds/sparse-table.md +++ b/docs/ds/sparse-table.md @@ -121,23 +121,23 @@ LCA(Least Common Ancestors)表示最近公共祖先。 对于一棵有根树,设 $LCA(u,v)=x$,则 $x$ 必须满足以下条件 -- $x$ 是 u 的祖先或 u +- $x$ 是 u 的祖先或 u -- $x$ 是 v 的祖先或 v +- $x$ 是 v 的祖先或 v -- $x$ 是在满足上面两个条件下深度最大的 +- $x$ 是在满足上面两个条件下深度最大的 显然,在一棵有根树内,对于任意两个节点有且仅有一个 $LCA$ 解决这个问题,我们通常有以下方法 -- 树上倍增(本文主要讲解此方法) +- 树上倍增(本文主要讲解此方法) -- 转化为 RMQ 问题 +- 转化为 RMQ 问题 -- 树链剖分 +- 树链剖分 -- Tarjan +- Tarjan ### 暴力做法 @@ -162,18 +162,16 @@ LCA(Least Common Ancestors)表示最近公共祖先。 一遍 DFS 计算即可 ```cpp -void dfs(int u,int father) -{ - dep[u]=dep[father]+1; //dep[x] 表示 x 的深度,在查询时会用到 - for (int i=0;i<=19;i++) - f[u][i+1]=f[f[u][i]][i]; // 预处理 - for (int i=first[u];i;i=next[i]) // 链式前向星 - { - int v=go[i]; - if (v==father) continue; - f[v][0]=u; //f[v][0] 表示 v 的父亲 - dfs(v,u); - } +void dfs(int u, int father) { + dep[u] = dep[father] + 1; // dep[x] 表示 x 的深度,在查询时会用到 + for (int i = 0; i <= 19; i++) f[u][i + 1] = f[f[u][i]][i]; // 预处理 + for (int i = first[u]; i; i = next[i]) // 链式前向星 + { + int v = go[i]; + if (v == father) continue; + f[v][0] = u; // f[v][0] 表示 v 的父亲 + dfs(v, u); + } } ``` @@ -183,27 +181,26 @@ void dfs(int u,int father) 只不过在跳的过程中从一步一步跳变成了 ** 一次跳多步 **。可以具体分为以下几步 -1. 让 $x$ 的深度比 $y$ 大(深度在预处理时已经求出) +1. 让 $x$ 的深度比 $y$ 大(深度在预处理时已经求出) -2. 将两个节点跳到同一深度。在此处我们使用二进制思想,依次尝试向上跳 $2^i,2^{i-1}\cdots 2^1,2^0$。如果发现则 $x$ 跳到了 $y$ 就说明 $LCA(x,y)=y$ +2. 将两个节点跳到同一深度。在此处我们使用二进制思想,依次尝试向上跳 $2^i,2^{i-1}\cdots 2^1,2^0$。如果发现则 $x$ 跳到了 $y$ 就说明 $LCA(x,y)=y$ -3. 一起往上跳。依然使用二进制思想,让他们一起往上跳 $2^i,2^{i-1}\cdots 2^1,2^0$. 如果 $f[x][i]!=f[y][i]$,说明 $x$ 和 $y$ 还未相遇。最后,$x$ 和 $y$ 必定只差一步相遇。这时 $x$ 的父亲即 $f[x][0]$ 就是他们的 LCA +3. 一起往上跳。依然使用二进制思想,让他们一起往上跳 $2^i,2^{i-1}\cdots 2^1,2^0$. 如果 $f[x][i]!=f[y][i]$,说明 $x$ 和 $y$ 还未相遇。最后,$x$ 和 $y$ 必定只差一步相遇。这时 $x$ 的父亲即 $f[x][0]$ 就是他们的 LCA ```cpp -int lca(int x,int y) -{ - if (dep[x]=0;i--) // 步骤 2 - { - if (dep[f[x][i]]>=dep[y]) x=f[x][i]; - if (x==y) return x; - } - for (int i=20;i>=0;i--) // 步骤 3 - if (f[x][i]!=f[y][i]) - { - x=f[x][i];y=f[y][i]; - } - return f[x][0]; +int lca(int x, int y) { + if (dep[x] < dep[y]) swap(x, y); // 步骤 1 + for (int i = 20; i >= 0; i--) // 步骤 2 + { + if (dep[f[x][i]] >= dep[y]) x = f[x][i]; + if (x == y) return x; + } + for (int i = 20; i >= 0; i--) // 步骤 3 + if (f[x][i] != f[y][i]) { + x = f[x][i]; + y = f[y][i]; + } + return f[x][0]; } ``` -- 2.11.0