OSDN Git Service

fix: 修了 poly-newton.md LaTeX 的锅以及线段树页面对储存空间的分析. (#1010)
authororzcyand1317 <36555123+orzcyand1317@users.noreply.github.com>
Mon, 4 Mar 2019 05:40:21 +0000 (13:40 +0800)
committerGitHub <noreply@github.com>
Mon, 4 Mar 2019 05:40:21 +0000 (13:40 +0800)
* fix: 修了个 LaTeX 公式的锅

忘记这是 Markdown 不是 Markdown GFM 了... qaq
以后一定记得空一行 qaq

* tmp

* Update segment.md

修复了线段树页面中对于堆式储存的错误分析.

* Update segment.md

添加了关于动态开点的描述.

* Update segment.md

* Delete segt7.png

* Delete segt10.png

* Delete segt11.png

* Delete segt12.png

* Delete segt13.png

* Delete segt14.png

* Delete segt15.png

* Delete segt6.png

* Delete segt8.png

* Delete segt9.png

* Add files via upload

relabel some images

* Update segment.md

* Update segment.md

补充了关于线段树堆式储存空间大小的描述.

* Update common-tricks.md

添加了「内存池」相关的内容

* Update common-tricks.md

* Update segment.md

* no need to use const& on int type

* Update segment.md

* Update common-tricks.md

* fix format

14 files changed:
docs/ds/images/segt10.png
docs/ds/images/segt11.png
docs/ds/images/segt12.png
docs/ds/images/segt13.png
docs/ds/images/segt14.png [deleted file]
docs/ds/images/segt15.png [deleted file]
docs/ds/images/segt5.png [new file with mode: 0644]
docs/ds/images/segt6.png
docs/ds/images/segt7.png
docs/ds/images/segt8.png
docs/ds/images/segt9.png
docs/ds/segment.md
docs/intro/common-tricks.md
docs/math/poly-newton.md

index 7ac0b69..a5564ba 100644 (file)
Binary files a/docs/ds/images/segt10.png and b/docs/ds/images/segt10.png differ
index 386ea5c..8061cf1 100644 (file)
Binary files a/docs/ds/images/segt11.png and b/docs/ds/images/segt11.png differ
index a5564ba..fbba13b 100644 (file)
Binary files a/docs/ds/images/segt12.png and b/docs/ds/images/segt12.png differ
index 8061cf1..df11fa4 100644 (file)
Binary files a/docs/ds/images/segt13.png and b/docs/ds/images/segt13.png differ
diff --git a/docs/ds/images/segt14.png b/docs/ds/images/segt14.png
deleted file mode 100644 (file)
index fbba13b..0000000
Binary files a/docs/ds/images/segt14.png and /dev/null differ
diff --git a/docs/ds/images/segt15.png b/docs/ds/images/segt15.png
deleted file mode 100644 (file)
index df11fa4..0000000
Binary files a/docs/ds/images/segt15.png and /dev/null differ
diff --git a/docs/ds/images/segt5.png b/docs/ds/images/segt5.png
new file mode 100644 (file)
index 0000000..934f6aa
Binary files /dev/null and b/docs/ds/images/segt5.png differ
index 934f6aa..ea24a80 100644 (file)
Binary files a/docs/ds/images/segt6.png and b/docs/ds/images/segt6.png differ
index 39a9697..9e31449 100644 (file)
Binary files a/docs/ds/images/segt7.png and b/docs/ds/images/segt7.png differ
index ea24a80..7ac0b69 100644 (file)
Binary files a/docs/ds/images/segt8.png and b/docs/ds/images/segt8.png differ
index 9e31449..386ea5c 100644 (file)
Binary files a/docs/ds/images/segt9.png and b/docs/ds/images/segt9.png differ
index a4e405b..a76a15d 100644 (file)
@@ -39,7 +39,7 @@ OI 中最常用的数据结构之一,不学不行啊!
 
  **思路如下:** ![](./images/segt2.png)![](./images/segt3.png)![](./images/segt4.png)
 
-此处给出 C++ 的代码实现,可参考注释理解:
+此处给出 C++ 的代码实现,可参考注释理解:
 
 ```cpp
 void build(int s, int t, int p) {
@@ -57,25 +57,27 @@ void build(int s, int t, int p) {
 
 上面那短短数行代码就能建立一个线段树。
 
-关于线段树的空间,如果采用堆式存储(堆式储存可以理解为 $2\times p$ 是 p 的左儿子, $2 \times p+1$ 是 p 的右儿子),d 数组的大小应为 $2n$ (叶子节点共有 $n$ 个,非叶子结点的个数不会超过叶子结点数量),上界是 $2n-1$。如果采用动态开点,则需要多开两个数组来记录左儿子和右儿子的编号/地址。
+关于线段树的空间: 如果采用堆式存储(堆式储存可以理解为 $2p$ 是 $p$ 的左儿子,$2p+1$ 是 $p$ 的右儿子),则 d 数组的长度应为 $2^{\left\lceil\log{n}\right\rceil+1}$,亦即取 $2$ 的幂中第一个大于等于 $n$ 的幂并将其乘二作为 d 数组的长度。
+
+分析: 容易知道线段树的深度是 $\left\lceil\log{n}\right\rceil$ 的,则在堆式储存情况下叶子节点(包括无用的叶子节点)数量为 $2^{\left\lceil\log{n}\right\rceil}$ 个,又由于其为一棵完全二叉树,则其总节点个数 $2^{\left\lceil\log{n}\right\rceil+1}-1$。当然如果你懒得计算的话可以直接把数组长度设为 $4n$,因为 $\frac{2^{\left\lceil\log{n}\right\rceil+1}-1}{n}$ 的最大值在 $n=2^{x}+1(x\in N_{+})$ 时取到,此时节点数为 $2^{\left\lceil\log{n}\right\rceil+1}-1=2^{x+2}-1=4n-5$。
+
+而如果采用动态开点的方法(动态开点即为不一次性开出全部节点的内存,而是在第一次使用到一个空节点时才开出其内存,这样可以减少空间复杂度的常数。动态开点的方法见[内存池](../../intro/common-tricks/#mempool)),由于不会存在堆式储存中空置节点的问题,故叶子节点数量为 $n$,非叶子节点数量为 $n-1$,只需要开 $2n-1$ 大小的数组即可。缺点是由于不使用堆式储存,无法知道其左右儿子的编号,故须新增两个域来储存其左右儿子的编号。
 
 ### 线段树的区间查询
 
 区间查询,比如求区间 $[l,r]$ 的总和(即 $a[l]+a[l+1]+ \cdots +a[r]$ )、求区间最大值/最小值……还有很多很多……怎么做呢?
 
-![](./images/segt6.png)
+![](./images/segt5.png)
 
 拿上面这张图举栗!
 
-![](./images/segt7.png)
-
 如果要查询区间 $[1,5]$ 的和,那直接获取 $d[1]$ 的值( $60$ )即可。那如果我就不查询区间 $[1,5]$ ,我就查区间 $[3,5]$ 呢?
 
 傻了吧。但其实呢我们肯定还是有办法的!
 
 你要查的不是 $[3,5]$ 吗?我把 $[3,5]$ 拆成 $[3,3]$ 和 $[4,5]$ 不就行了吗?
 
-此处给出 C++ 的代码实现,可参考注释理解:
+此处给出 C++ 的代码实现 ,可参考注释理解:
 
 ```cpp
 int getsum(int l, int r, int s, int t, int p) {
@@ -120,32 +122,32 @@ int getsum(int l, int r, int s, int t, int p) {
 
 如图:
 
+![](./images/segt6.png)
+
+![](./images/segt7.png)
+
 ![](./images/segt8.png)
 
 ![](./images/segt9.png)
 
 ![](./images/segt10.png)
 
-![](./images/segt11.png)
-
-![](./images/segt12.png)
-
 注:这里 D 表示当前节点的值(即所表示区间的区间和)。
-为什么节点 A 的 D 是 $2\times (1000000000000001\bmod 2)$ 呢?原因很简单节点 A 表示的区间是 $[1,2]$ ,一共包含 $2$ 个元素。我们是让 $[1,2]$ 这个区间的每个元素都加上 $1000000000000001\bmod 2$ ,所以节点 A 的值就加上了 $2\times (1000000000000001\bmod 2)$ 咯。
+为什么节点 A 的 D 是 $2\times (1000000000000001\bmod 2)$ 呢?原因很简单节点 A 表示的区间是 $[1,2]$ ,一共包含 $2$ 个元素。我们是让 $[1,2]$ 这个区间的每个元素都加上 $1000000000000001\bmod 2$ ,所以节点 A 的值就加上了 $2\times (1000000000000001\bmod 2)$ 咯。
 
 如果这时候我们要查询区间 $[1,1]$ (即节点 B 的值)怎么办呢?不是说了吗?如果 B 要用到的时候,A 就把它欠的还给 B!
 
 具体是这样操作(如图):
 
-![](./images/segt13.png)
+![](./images/segt11.png)
 
-![](./images/segt14.png)
+![](./images/segt12.png)
 
 注:为什么是加上 $1\times (1000000000000001\bmod 2)$ 呢?
 
 原因和上面一样——B 和 C 表示的区间中只有 $1$ 个元素啊!
 
-![](./images/segt15.png)
+![](./images/segt13.png)
 
 由此我们可以得到,区间 $[1,1]$ 的区间和就是 $1$ 啦!O(∩\_∩)O 哈哈~!
 
index a684f4e..0d0cb91 100644 (file)
@@ -175,3 +175,32 @@ int main()
  }
 }
 ```
+
+## <span id="mempool">内存池</span>
+
+当我们需要动态分配内存的时候,频繁使用 new/malloc 会占用大量的时间和空间,甚至生成大量的内存碎片从而降低程序的性能,可能会使原本正确的程序 TLE/MLE。
+
+这时候我们就需要使用到「内存池」这种技巧: 在真正使用内存之前,先申请分配一定大小的内存作为备用,当需要动态分配时则直接从备用内存中分配一块即可。
+
+当然在大多数 OI 题当中,我们可以预先算出需要使用到的最大内存并一次性申请分配。
+
+如申请动态分配 $32$ 位有符号整数数组的代码:
+
+```cpp
+inline int* newarr(int sz){
+       static int pool[maxn],*allocp=pool;
+       return allocp+=sz,allocp-sz;
+}
+
+```
+
+线段树动态开点的代码:
+
+```cpp
+inline Node* newnode(){
+       static Node pool[maxn<<1],*allocp=pool-1;
+       return++allocp;
+}
+
+```
+
index 7a35c1c..391a32b 100644 (file)
@@ -25,6 +25,7 @@ $$\forall 2\leqslant i:\left(f\left(x\right)-f_{0}\left(x\right)\right)^{i}\equi
 则:
 
 $$\sum_{i=0}^{+\infty}\frac{g^{\left(i\right)}\left(f_{0}\left(x\right)\right)}{i!}\left(f\left(x\right)-f_{0}\left(x\right)\right)^{i}\equiv g\left(f_{0}\left(x\right)\right)+g'\left(f_{0}\left(x\right)\right)\left(f\left(x\right)-f_{0}\left(x\right)\right)\equiv 0\pmod{x^{n}}$$
+
 $$f\left(x\right)\equiv f_{0}\left(x\right)-\frac{g\left(f_{0}\left(x\right)\right)}{g'\left(f_{0}\left(x\right)\right)}\pmod{x^{n}}$$
 
 ## Examples