Loading README.md +3 −5 Original line number Diff line number Diff line Loading @@ -4,12 +4,10 @@ [](https://travis-ci.org/OI-wiki/OI-wiki) [](https://status.oi-wiki.org/) [](https://t.me/OIwiki) [](https://jq.qq.com/?_wv=1027&k=5EfkM6K) [-brightgreen?style=flat-square)](https://feathub.com/OI-wiki/OI-wiki) [](https://t.me/OIwiki) [](https://jq.qq.com/?_wv=1027&k=5EfkM6K) [](https://github.com/OI-wiki/OI-wiki) [](https://github.com/OI-wiki/OI-wiki) <!-- [](https://github.com/OI-wiki/OI-wiki) --> * * * Loading @@ -23,7 +21,7 @@ 目前,**OI Wiki** 的内容还有很多不完善的地方,知识点覆盖不够全面,存在一些低质量页面需要修改。**OI Wiki** 团队以及参与贡献的小伙伴们正在积极完善这些内容。 关于上述待完善内容,请参见 **OI Wiki** 的 [Issues](https://github.com/OI-wiki/OI-wiki/issues)、 [迭代计划](https://github.com/OI-wiki/OI-wiki/labels/%E8%BF%AD%E4%BB%A3%E8%AE%A1%E5%88%92%20%2F%20Iteration%20Plan) 和 [FeatHub 页面](https://feathub.com/OI-wiki/OI-wiki)。 关于上述待完善内容,请参见 **OI Wiki** 的 [Issues](https://github.com/OI-wiki/OI-wiki/issues) 以及 [迭代计划](https://github.com/OI-wiki/OI-wiki/labels/%E8%BF%AD%E4%BB%A3%E8%AE%A1%E5%88%92%20%2F%20Iteration%20Plan)。 与此同时, **OI Wiki** 源于社区,提倡 **知识自由**,在未来也绝不会商业化,将始终保持独立自由的性质。 Loading docs/basic/index.md +2 −10 Original line number Diff line number Diff line 介绍一些基础知识,为之后的进阶内容做铺垫。 介绍一些基础算法,为之后的进阶内容做铺垫。 - [x] 枚举 - [x] 模拟 - [x] 递归 & 分治 - [x] 贪心 - [x] 排序 - [x] 二分 - [x] 倍增 - [x] 构造 - [x] 前缀和 & 差分 一方面,这些内容可以让初学者对 OI 的一些思想有初步的认识;另一方面,其中的很多算法还会在以后的进阶内容中得到运用。 docs/ds/seg.md +4 −4 Original line number Diff line number Diff line Loading @@ -496,13 +496,13 @@ int getsum(int l, int r, int s, int t, int p) { 但是有一个问题在于普通线段树的区间询问在某些毒瘤的眼里可能还是有些慢了。 简单来说就是线段树建树的时候需要做 $O(n)$ 次合并操作,而每一次区间询问需要做 $O(\log{n})$ 次合并操作,询问区间和这种东西的时候还可以忍受,但是当我们需要询问区间线性基这种合并复杂度高达 $O(\log^2{n})$ 的信息的话,此时就算是做 $O(\log{n})$ 次合并有些时候在时间上也是不可接受的。 简单来说就是线段树建树的时候需要做 $O(n)$ 次合并操作,而每一次区间询问需要做 $O(\log{n})$ 次合并操作,询问区间和这种东西的时候还可以忍受,但是当我们需要询问区间线性基这种合并复杂度高达 $O(\log^2{w})$ 的信息的话,此时就算是做 $O(\log{n})$ 次合并有些时候在时间上也是不可接受的。 而所谓 "猫树" 就是一种不支持修改,仅仅支持快速区间询问的一种静态线段树。 构造一棵这样的静态线段树需要 $O(n\log{n})$ 次合并操作,但是此时的查询复杂度被加速至 $O(1)$ 次合并操作。 在处理线性基这样特殊的信息的时候甚至可以将复杂度降至 $O(n\log^2{n})$ 。 在处理线性基这样特殊的信息的时候甚至可以将复杂度降至 $O(n\log^2{w})$ 。 ### 原理 Loading @@ -516,7 +516,7 @@ int getsum(int l, int r, int s, int t, int p) { 由于 $p$ 是 $l$ 和 $r$ 的 lca,这意味着 $p$ 的左儿子是 $l$ 的祖先而不是 $r$ 的祖先, $p$ 的右儿子是 $r$ 的祖先而不是 $l$ 的祖先。 因此 $l$ 一定在 $[L,MID]$ 这个区间内, $r$ 一定在 $[MID,R]$ 这个区间内。 因此 $l$ 一定在 $[L,MID]$ 这个区间内, $r$ 一定在 $(MID,R]$ 这个区间内。 有了这两个性质,我们就可以将询问的复杂度降至 $O(1)$ 了。 Loading Loading @@ -554,7 +554,7 @@ int getsum(int l, int r, int s, int t, int p) { 这样我们就完成了一个猫树。 由于建树的时候涉及到求前缀和和求后缀和,所以对于线性基这种虽然合并是 $O(\log^2{n})$ 但是求前缀和却是 $O(n\log{n})$ 的信息,使用猫树可以将静态区间线性基从 $O(n\log^2{n}+m\log^3{n})$ 优化至 $O(n\log^2{n}+m\log^2{n})$ 的复杂度。 由于建树的时候涉及到求前缀和和求后缀和,所以对于线性基这种虽然合并是 $O(\log^2{w})$ 但是求前缀和却是 $O(n\log{n})$ 的信息,使用猫树可以将静态区间线性基从 $O(n\log^2{w}+m\log^2{w}\log{n})$ 优化至 $O(n\log{n}\log{w}+m\log^2{w})$ 的复杂度。 ### 参考 Loading docs/graph/tree-centroid.md 0 → 100644 +45 −0 Original line number Diff line number Diff line author: Ir1d, TrisolarisHD, LucienShui, Anguei ## 定义 对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心。 (这里以及下文中的“子树”都是指无根树的子树,即包括“向上”的那棵子树,并且不包括整棵树自身。) ## 性质 以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。 ## 求法 在 DFS 中计算每个子树的大小,记录“向下”的子树的最大大小,利用总点数 - 当前子树(这里的子树指有根树的子树)的大小得到“向上”的子树的大小,然后就可以依据定义找到重心了。 ???+note "参考代码" ```cpp void getCentroid(int u, int fa) { siz[u] = 1; wt[u] = 0; for (int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if (v != fa) { getCentroid(v, u); siz[u] += siz[v]; wt[u] = max(wt[u], siz[v]); } } wt[u] = max(wt[u], n - siz[u]); if (rt == 0 || wt[u] < wt[rt]) rt = u; // rt 为重心编号 } ``` ## 参考 <http://fanhq666.blog.163.com/blog/static/81943426201172472943638/> <https://www.cnblogs.com/zinthos/p/3899075.html> docs/graph/tree-misc.mddeleted 100644 → 0 +0 −65 Original line number Diff line number Diff line ## 树的重心 ### 定义 以树的重心为根时,所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。 删去重心后,生成的多棵树尽可能平衡。 ### 性质 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两个树的重心的路径上。 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。 ### 求法 树的重心可以通过简单的两次搜索求出。 1. 第一遍搜索求出以每个节点为根的子树中结点数量 $sz_{u}$ 2. 第二遍搜索找出使 $\max_{v\in\operatorname{son}(u)}\{n-sz_{u},sz_{v}\}$ 最小的节点 $u$ 。 实际上这两步操作可以在一次遍历中解决。对节点 u 的每一个儿子 v 递归处理,然后以 $sz_{v}$ 更新 $u$ 的子节点子树节点数最大值,处理完所有子结点后,判断 u 是否为重心。 (代码来自叉姐) ```cpp struct CenterTree { int n; int ans; int siz; int son[maxn]; void dfs(int u, int pa) { son[u] = 1; int res = 0; for (int i = head[u]; ~i; i = edges[i].next) { int v = edges[i].to; if (v == pa) continue; dfs(v, u); son[u] += son[v]; res = max(res, son[v]); } res = max(res, n - son[u]); if (res < siz) { ans = u; siz = res; } } int getCenter(int x) { ans = 0; siz = INF; dfs(x, -1); return ans; } } ``` ## 参考 <http://fanhq666.blog.163.com/blog/static/81943426201172472943638/> <https://www.cnblogs.com/zinthos/p/3899075.html> Loading
README.md +3 −5 Original line number Diff line number Diff line Loading @@ -4,12 +4,10 @@ [](https://travis-ci.org/OI-wiki/OI-wiki) [](https://status.oi-wiki.org/) [](https://t.me/OIwiki) [](https://jq.qq.com/?_wv=1027&k=5EfkM6K) [-brightgreen?style=flat-square)](https://feathub.com/OI-wiki/OI-wiki) [](https://t.me/OIwiki) [](https://jq.qq.com/?_wv=1027&k=5EfkM6K) [](https://github.com/OI-wiki/OI-wiki) [](https://github.com/OI-wiki/OI-wiki) <!-- [](https://github.com/OI-wiki/OI-wiki) --> * * * Loading @@ -23,7 +21,7 @@ 目前,**OI Wiki** 的内容还有很多不完善的地方,知识点覆盖不够全面,存在一些低质量页面需要修改。**OI Wiki** 团队以及参与贡献的小伙伴们正在积极完善这些内容。 关于上述待完善内容,请参见 **OI Wiki** 的 [Issues](https://github.com/OI-wiki/OI-wiki/issues)、 [迭代计划](https://github.com/OI-wiki/OI-wiki/labels/%E8%BF%AD%E4%BB%A3%E8%AE%A1%E5%88%92%20%2F%20Iteration%20Plan) 和 [FeatHub 页面](https://feathub.com/OI-wiki/OI-wiki)。 关于上述待完善内容,请参见 **OI Wiki** 的 [Issues](https://github.com/OI-wiki/OI-wiki/issues) 以及 [迭代计划](https://github.com/OI-wiki/OI-wiki/labels/%E8%BF%AD%E4%BB%A3%E8%AE%A1%E5%88%92%20%2F%20Iteration%20Plan)。 与此同时, **OI Wiki** 源于社区,提倡 **知识自由**,在未来也绝不会商业化,将始终保持独立自由的性质。 Loading
docs/basic/index.md +2 −10 Original line number Diff line number Diff line 介绍一些基础知识,为之后的进阶内容做铺垫。 介绍一些基础算法,为之后的进阶内容做铺垫。 - [x] 枚举 - [x] 模拟 - [x] 递归 & 分治 - [x] 贪心 - [x] 排序 - [x] 二分 - [x] 倍增 - [x] 构造 - [x] 前缀和 & 差分 一方面,这些内容可以让初学者对 OI 的一些思想有初步的认识;另一方面,其中的很多算法还会在以后的进阶内容中得到运用。
docs/ds/seg.md +4 −4 Original line number Diff line number Diff line Loading @@ -496,13 +496,13 @@ int getsum(int l, int r, int s, int t, int p) { 但是有一个问题在于普通线段树的区间询问在某些毒瘤的眼里可能还是有些慢了。 简单来说就是线段树建树的时候需要做 $O(n)$ 次合并操作,而每一次区间询问需要做 $O(\log{n})$ 次合并操作,询问区间和这种东西的时候还可以忍受,但是当我们需要询问区间线性基这种合并复杂度高达 $O(\log^2{n})$ 的信息的话,此时就算是做 $O(\log{n})$ 次合并有些时候在时间上也是不可接受的。 简单来说就是线段树建树的时候需要做 $O(n)$ 次合并操作,而每一次区间询问需要做 $O(\log{n})$ 次合并操作,询问区间和这种东西的时候还可以忍受,但是当我们需要询问区间线性基这种合并复杂度高达 $O(\log^2{w})$ 的信息的话,此时就算是做 $O(\log{n})$ 次合并有些时候在时间上也是不可接受的。 而所谓 "猫树" 就是一种不支持修改,仅仅支持快速区间询问的一种静态线段树。 构造一棵这样的静态线段树需要 $O(n\log{n})$ 次合并操作,但是此时的查询复杂度被加速至 $O(1)$ 次合并操作。 在处理线性基这样特殊的信息的时候甚至可以将复杂度降至 $O(n\log^2{n})$ 。 在处理线性基这样特殊的信息的时候甚至可以将复杂度降至 $O(n\log^2{w})$ 。 ### 原理 Loading @@ -516,7 +516,7 @@ int getsum(int l, int r, int s, int t, int p) { 由于 $p$ 是 $l$ 和 $r$ 的 lca,这意味着 $p$ 的左儿子是 $l$ 的祖先而不是 $r$ 的祖先, $p$ 的右儿子是 $r$ 的祖先而不是 $l$ 的祖先。 因此 $l$ 一定在 $[L,MID]$ 这个区间内, $r$ 一定在 $[MID,R]$ 这个区间内。 因此 $l$ 一定在 $[L,MID]$ 这个区间内, $r$ 一定在 $(MID,R]$ 这个区间内。 有了这两个性质,我们就可以将询问的复杂度降至 $O(1)$ 了。 Loading Loading @@ -554,7 +554,7 @@ int getsum(int l, int r, int s, int t, int p) { 这样我们就完成了一个猫树。 由于建树的时候涉及到求前缀和和求后缀和,所以对于线性基这种虽然合并是 $O(\log^2{n})$ 但是求前缀和却是 $O(n\log{n})$ 的信息,使用猫树可以将静态区间线性基从 $O(n\log^2{n}+m\log^3{n})$ 优化至 $O(n\log^2{n}+m\log^2{n})$ 的复杂度。 由于建树的时候涉及到求前缀和和求后缀和,所以对于线性基这种虽然合并是 $O(\log^2{w})$ 但是求前缀和却是 $O(n\log{n})$ 的信息,使用猫树可以将静态区间线性基从 $O(n\log^2{w}+m\log^2{w}\log{n})$ 优化至 $O(n\log{n}\log{w}+m\log^2{w})$ 的复杂度。 ### 参考 Loading
docs/graph/tree-centroid.md 0 → 100644 +45 −0 Original line number Diff line number Diff line author: Ir1d, TrisolarisHD, LucienShui, Anguei ## 定义 对于树上的每一个点,计算其所有子树中最大的子树节点数,这个值最小的点就是这棵树的重心。 (这里以及下文中的“子树”都是指无根树的子树,即包括“向上”的那棵子树,并且不包括整棵树自身。) ## 性质 以树的重心为根时,所有子树的大小都不超过整棵树大小的一半。 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。 ## 求法 在 DFS 中计算每个子树的大小,记录“向下”的子树的最大大小,利用总点数 - 当前子树(这里的子树指有根树的子树)的大小得到“向上”的子树的大小,然后就可以依据定义找到重心了。 ???+note "参考代码" ```cpp void getCentroid(int u, int fa) { siz[u] = 1; wt[u] = 0; for (int i = head[u]; ~i; i = nxt[i]) { int v = to[i]; if (v != fa) { getCentroid(v, u); siz[u] += siz[v]; wt[u] = max(wt[u], siz[v]); } } wt[u] = max(wt[u], n - siz[u]); if (rt == 0 || wt[u] < wt[rt]) rt = u; // rt 为重心编号 } ``` ## 参考 <http://fanhq666.blog.163.com/blog/static/81943426201172472943638/> <https://www.cnblogs.com/zinthos/p/3899075.html>
docs/graph/tree-misc.mddeleted 100644 → 0 +0 −65 Original line number Diff line number Diff line ## 树的重心 ### 定义 以树的重心为根时,所有的子树(不算整个树自身)的大小都不超过整个树大小的一半。 找到一个点,其所有的子树中最大的子树节点数最少,那么这个点就是这棵树的重心。 删去重心后,生成的多棵树尽可能平衡。 ### 性质 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么他们的距离和一样。 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两个树的重心的路径上。 在一棵树上添加或删除一个叶子,那么它的重心最多只移动一条边的距离。 ### 求法 树的重心可以通过简单的两次搜索求出。 1. 第一遍搜索求出以每个节点为根的子树中结点数量 $sz_{u}$ 2. 第二遍搜索找出使 $\max_{v\in\operatorname{son}(u)}\{n-sz_{u},sz_{v}\}$ 最小的节点 $u$ 。 实际上这两步操作可以在一次遍历中解决。对节点 u 的每一个儿子 v 递归处理,然后以 $sz_{v}$ 更新 $u$ 的子节点子树节点数最大值,处理完所有子结点后,判断 u 是否为重心。 (代码来自叉姐) ```cpp struct CenterTree { int n; int ans; int siz; int son[maxn]; void dfs(int u, int pa) { son[u] = 1; int res = 0; for (int i = head[u]; ~i; i = edges[i].next) { int v = edges[i].to; if (v == pa) continue; dfs(v, u); son[u] += son[v]; res = max(res, son[v]); } res = max(res, n - son[u]); if (res < siz) { ans = u; siz = res; } } int getCenter(int x) { ans = 0; siz = INF; dfs(x, -1); return ans; } } ``` ## 参考 <http://fanhq666.blog.163.com/blog/static/81943426201172472943638/> <https://www.cnblogs.com/zinthos/p/3899075.html>