OSDN Git Service

arrange formula (#127)
authorXeonacid <h.dwwwwww@gmail.com>
Sun, 19 Aug 2018 05:26:33 +0000 (13:26 +0800)
committerGitHub <noreply@github.com>
Sun, 19 Aug 2018 05:26:33 +0000 (13:26 +0800)
29 files changed:
docs/basic/sort.md
docs/data-structure/intermediate/segment.md
docs/data-structure/intermediate/virtual-tree.md
docs/data-structure/novice/dsu.md
docs/data-structure/novice/heap.md
docs/data-structure/novice/square-root-decomposition.md
docs/dp/index.md
docs/graph/basic.md
docs/graph/mst.md
docs/graph/tree-basic.md
docs/math/combination.md
docs/math/crt.md
docs/math/euler.md
docs/math/fft.md
docs/math/fwt.md
docs/math/gauss.md
docs/math/gcd.md
docs/math/ntt.md
docs/math/prime.md
docs/math/quick-pow.md
docs/math/sieve.md
docs/math/stirling.md
docs/misc/mo-algo.md
docs/search/astar.md
docs/string/ac-automaton.md
docs/string/hash.md
docs/string/kmp.md
docs/string/match.md
docs/string/trie.md

index 3055542..b370cca 100644 (file)
@@ -14,9 +14,9 @@
 
 时间复杂度分为最坏时间复杂度、平均时间复杂度、最好时间复杂度等等。OI 竞赛中要考虑的一般是最坏时间复杂度,因为它代表的是算法运行水平的下界,在评测中不会出现更差的结果了。
 
-基于比较的排序算法的时间复杂度是 $\mathcal{O}(n\log n)$ 的。
+基于比较的排序算法的时间复杂度下限是 $O(n\log n)$ 的。
 
-当然也有不是 $\mathcal{O}(n\log n)$ 的,桶排序的时间复杂度是 $\mathcal{O}(n)$,但是它是在「用空间换时间」,它的空间复杂度是 $\mathcal{O}(所排序的最大数)$
+当然也有不是 $O(n\log n)$ 的,桶排序的时间复杂度是 $O(n)$,但是它是在「用空间换时间」,它的空间复杂度是 $O(所排序的最大数)$
 
 ## 归并排序
 
index b827169..8ab1deb 100644 (file)
@@ -4,12 +4,12 @@
 OI 中最常用的数据结构之一,不学不行啊OvO\r
 \r
 ## 2. 线段树是什么\r
-> 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为 $\mathcal{O}(\log N)$。而未优化的空间复杂度为 $2N$,因此有时需要离散化让空间压缩。——From 度娘\r
+> 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为 $O(\log N)$。而未优化的空间复杂度为 $2N$,因此有时需要离散化让空间压缩。——From 度娘\r
 \r
 反正就是一种可以在很短的时间内对某个区间进行操作的数据结构。\r
 \r
 ## 3. 线段树有什么用\r
-在 $\mathcal{O}(\log N)$ 的时间复杂度内实现如:单点修改、区间修改、区间查询(如:区间求和,求区间最大值,求区间最小值……)还有很多……\r
+在 $O(\log N)$ 的时间复杂度内实现如:单点修改、区间修改、区间查询(如:区间求和,求区间最大值,求区间最小值……)还有很多……\r
 \r
 ## 4. 线段树怎么实现\r
 ### (1) 线段树的基本结构与建树\r
@@ -21,9 +21,9 @@ OI 中最常用的数据结构之一,不学不行啊OvO
 ![](./images/segt1.png)\r
 \r
 \r
-图中 $d[1]$ 表示根节点,紫色方框是数组 $a$,红色方框是数组 $d$,红色方框中的括号中的黄色数字表示它所在的那个红色方框表示的线段树节点所表示的区间,如 $d[1]$ 所表示的区间就是 $1\sim 5\ (a[1]\sim a[5])$,即 $d[1]$ 所保存的值是 $a[1]+a[2]+...+a[5]$,$d[1]=60$ 表示的是 $a[1]+a[2]+...+a[5]=60$。\r
+图中 $d[1]$ 表示根节点,紫色方框是数组 $a$,红色方框是数组 $d$,红色方框中的括号中的黄色数字表示它所在的那个红色方框表示的线段树节点所表示的区间,如 $d[1]$ 所表示的区间就是 $1\sim 5\ (a[1]\sim a[5])$,即 $d[1]$ 所保存的值是 $a[1]+a[2]+ \cdots +a[5]$,$d[1]=60$ 表示的是 $a[1]+a[2]+ \cdots +a[5]=60$。\r
 \r
-通过观察我们不难发现,$d[i]$ 的左儿子节点就是 $d[2\times i]$,$d[i]$ 的右节点就是 $d[2\times i+1]$。进一步观察,可以看出如果 $d[i]$ 表示的是区间 $[s,t]$(即 $d[i]=a[s]+a[s+1]+...+a[t] $)的话,那么 $d[i]$ 的左儿子节点表示的是区间 $[s,(s+t)/2]$,$d[i]$ 的右儿子表示的是区间 $[(s+t)/2+1,t]$。\r
+通过观察我们不难发现,$d[i]$ 的左儿子节点就是 $d[2\times i]$,$d[i]$ 的右节点就是 $d[2\times i+1]$。进一步观察,可以看出如果 $d[i]$ 表示的是区间 $[s,t]$(即 $d[i]=a[s]+a[s+1]+ \cdots +a[t] $)的话,那么 $d[i]$ 的左儿子节点表示的是区间 $[s, \frac{s+t}{2} ]$,$d[i]$ 的右儿子表示的是区间 $[ \frac{s+t}{2} +1,t]$。\r
 \r
 为什么要这样表示呢?因为线段树利用了二分的思想,线段树实际上是个二叉树,这些不懂的话就无法理解线段树了,所以如果不明白二分或者二叉树的话……建议去问问度娘。\r
 \r
@@ -79,7 +79,7 @@ void build(int s,int t,int p)
 ![](./images/segt5.png)\r
 \r
 ### (2) 线段树的区间查询\r
-区间查询,比如求区间 $[l,r]$ 的总和(即 $a[l]+a[l+1]+...+a[r]$)、求区间最大值/最小值……还有很多很多……怎么做呢?\r
+区间查询,比如求区间 $[l,r]$ 的总和(即 $a[l]+a[l+1]+ \cdots +a[r]$)、求区间最大值/最小值……还有很多很多……怎么做呢?\r
 \r
 ![](./images/segt6.png)\r
 \r
@@ -232,7 +232,7 @@ int getsum(int l,int r,int s,int t,int p)//l是查询的区间左端点,r是
 }\r
 ```\r
 \r
-你有没有发现区间查询和区间修改很像吗?(...^\_\_^...) \r
+你有没有发现区间查询和区间修改很像吗?(...^\_\_^...)\r
 \r
 嘻嘻……其实平时我打线段树区间修改和查询我都是打一份,另一份复制黏贴以后再稍作修改就行了。\r
 \r
@@ -337,7 +337,7 @@ int main()
         if(i1==2)cout<<getsum(i2,i3,1,n,1)<<endl;\r
         else cin>>i4,update(i2,i3,i4,1,n,1);\r
     }\r
-    return 0; \r
+    return 0;\r
 }\r
 ```\r
 \r
@@ -443,7 +443,7 @@ int main()
         }\r
     }\r
        return 0;\r
-} \r
+}\r
 ```\r
 \r
 ### (3) CODEVS 线段树练习 (这是一个系列)\r
@@ -506,7 +506,7 @@ int main()
         if(i1==0)cout<<getsum(i2,i3,1,n,1)<<endl;\r
         else cin>>i4,update(i2,i3,i4,1,n,1);\r
     }\r
-    return 0; \r
+    return 0;\r
 }\r
 ```\r
 \r
index 7edf65e..a6f4569 100644 (file)
@@ -18,7 +18,7 @@
 
 ### Output
 
-输出有 $m$ 行,分别代表每次任务的最小代价。 
+输出有 $m$ 行,分别代表每次任务的最小代价。
 
 ### Sample Input
 
 
 不难发现虚树中祖先 -> 后代的关系并不会改变。(就是不会出现原本 $a$ 是 $b$ 的祖先结果后面 $a$ 变成 $b$ 的后代了之类的鬼事)
 
-但我们不可能 $\mathcal{O}(k^2)$ 暴力枚举 LCA,所以我们不难想到——首先将关键点按 DFS 序排序,然后排完序以后相邻的两个关键点(相邻指的是在排序后的序列中下表差值的绝对值等于1)求一下 LCA,并把它加入虚树。
+但我们不可能 $O(k^2)$ 暴力枚举 LCA,所以我们不难想到——首先将关键点按 DFS 序排序,然后排完序以后相邻的两个关键点(相邻指的是在排序后的序列中下表差值的绝对值等于1)求一下 LCA,并把它加入虚树。
 
 因为可能多个节点的 LCA 可能是同一个,所以我们不能多次将它加入虚树。
 
index 6f26a8d..44ecc70 100644 (file)
@@ -93,11 +93,11 @@ void unionSet(int x,int y)
 \r
 ### 时间复杂度\r
 \r
-同时使用路径压缩和启发式合并之后,并查集的每个操作平均时间仅为 $\mathcal{O}(\alpha(n))$ ,其中 $\alpha$ 为 [阿克曼函数](https://en.wikipedia.org/wiki/Ackermann_function) 的反函数,其增长极其缓慢,也就是说其平均运行时间可以认为是一个含笑的常数。 \r
+同时使用路径压缩和启发式合并之后,并查集的每个操作平均时间仅为 $O(\alpha(n))$ ,其中 $\alpha$ 为 [阿克曼函数](https://en.wikipedia.org/wiki/Ackermann_function) 的反函数,其增长极其缓慢,也就是说其平均运行时间可以认为是一个含笑的常数。 \r
 \r
 ### 空间复杂度\r
 \r
-显然为 $\mathcal{O}(n)$。\r
+显然为 $O(n)$。\r
 \r
 ## 其他应用\r
 \r
index 4c80488..38b4cf8 100644 (file)
 
 - 二叉堆 *(binary heap)*
 
-最基础的堆,不支持 merge 和可持久化,所有操作的复杂度都是 $\mathcal{O}(\log n)$ 的。
+最基础的堆,不支持 merge 和可持久化,所有操作的复杂度都是 $O(\log n)$ 的。
 
 - 二项堆 *(binomial heap)*
 
-支持 merge 的堆,(也能可持久化),所有操作的复杂度都是 $\mathcal{O}(\log n)$。
+支持 merge 的堆,(也能可持久化),所有操作的复杂度都是 $O(\log n)$。
 
 - Fib 堆 *(Fibonacci heap)*
 
-除了不能可持久化,支持全部功能,而且除了 deletemin 以外都是均摊 $\mathcal{O}(1)$ 的。
+除了不能可持久化,支持全部功能,而且除了 deletemin 以外都是均摊 $O(1)$ 的。
 
 ## 二叉堆
 
@@ -52,7 +52,7 @@
 
 可以证明,插入之后向上调整后,没有其他结点会不满足堆性质。
 
-向上调整的时间复杂度是 $\mathcal{O}(\log n)$ 的。
+向上调整的时间复杂度是 $O(\log n)$ 的。
 
 ### 删除操作
 
 
 可以证明,删除并向下调整后,没有其他结点不满足堆性质。
 
-时间复杂度 $\mathcal{O}(\log n)$。
+时间复杂度 $O(\log n)$。
 
 ### 减小某个点的权值
 
-很显然,直接修改后,向上调整一次即可,时间复杂度为 $\mathcal{O}(\log n)$。
+很显然,直接修改后,向上调整一次即可,时间复杂度为 $O(\log n)$。
 
 ### 实现
 
@@ -104,7 +104,7 @@ down(x) {
 
 考虑这么一个问题,从一个空的堆开始,插入 $n$ 个元素,不在乎顺序。
 
-直接一个一个插入需要 $\mathcal{O}(n \log n)$ 的时间,有没有更好的方法?
+直接一个一个插入需要 $O(n \log n)$ 的时间,有没有更好的方法?
 
 #### 方法一:使用 decreasekey(即,向上调整)
 
@@ -116,7 +116,7 @@ build_heap_1() {
 }
 ```
 
-为啥这么做:对于第 $k$ 层的结点,向上调整的复杂度为 $\mathcal{O}(k)$ 而不是 $\mathcal{O}(\log n)$。
+为啥这么做:对于第 $k$ 层的结点,向上调整的复杂度为 $O(k)$ 而不是 $O(\log n)$。
 
 总复杂度:$\log 1 + \log 2 + \cdots + \log n = \Theta(n \log n)$。
 
@@ -134,7 +134,7 @@ build_heap_2() {
 
 换一种理解方法,每次「合并」两个已经调整好的堆,这说明了正确性。
 
-注意到向下调整的复杂度,为 $\mathcal{O}(\log n - k)$。
+注意到向下调整的复杂度,为 $O(\log n - k)$。
 
 $$
 \begin{aligned}
@@ -142,10 +142,10 @@ $$
 & \leq n \log n - 0 \times 2^0 - 1 \times 2^1 -\cdots - (\log n - 1) \times \frac{n}{2} \\\\
 & = n \log n - (n-1) - (n-2) - (n-4) - \cdots - (n-\frac{n}{2}) \\\\
 & = n \log n - n \log n + 1 + 2 + 4 + \cdots + \frac{n}{2} \\\\
-& = n - 1 \\\\ &  = \mathcal{O}(n)
+& = n - 1 \\\\ &  = O(n)
 \end{aligned}
 $$
 
-之所以能 $\mathcal{O}(n)$ 建堆,是因为堆性质很弱,二叉堆并不是唯一的。
+之所以能 $O(n)$ 建堆,是因为堆性质很弱,二叉堆并不是唯一的。
 
-要是像排序那样的强条件就难说了。
\ No newline at end of file
+要是像排序那样的强条件就难说了。
index d657ae0..8aad6c2 100644 (file)
 
 区间修改:同样涉及这些东西,使用打标记和暴力修改,同样的复杂度。
 
-当 $T=\sqrt{n}$ 时,复杂度 $\mathcal{O}(\sqrt{n})$。
+当 $T=\sqrt{n}$ 时,复杂度 $O(\sqrt{n})$。
 
 ## 区间和 2
 
-上一个做法的复杂度是 $\mathcal{O}(1) - \mathcal{O}(\sqrt{n})$。
+上一个做法的复杂度是 $\Omega(1) , O(\sqrt{n})$。
 
-我们在这里介绍一种 $\mathcal{O}(\sqrt{n}) - \mathcal{O}(1)$ 的算法。
+我们在这里介绍一种 $O(\sqrt{n}) - O(1)$ 的算法。
 
-为了 $\mathcal{O}(1)$ 询问,我们可以维护各种前缀和。
+为了 $O(1)$ 询问,我们可以维护各种前缀和。
 
 然而在有修改的情况下,不方便维护,只能维护单个块内的前缀和。
 
 以及整块作为一个单位的前缀和。
 
-每次修改 $\mathcal{O}(T+\frac{n}{T})$。
+每次修改 $O(T+\frac{n}{T})$。
 
-询问:涉及三部分,每部分都可以直接通过前缀和得到,时间复杂度 $\mathcal{O}(1)$。
+询问:涉及三部分,每部分都可以直接通过前缀和得到,时间复杂度 $O(1)$。
 
 ## 对询问分块
 
 
 如果操作数量比较少,我们可以把操作记下来,在询问的时候加上这些操作的影响。
 
-假设最多记录 $T$ 个操作,则修改 $\mathcal{O}(1)$,询问 $\mathcal{O}(T)$。
+假设最多记录 $T$ 个操作,则修改 $O(1)$,询问 $O(T)$。
 
 $T$ 个操作之后,重新计算前缀和,$O(n)$。
 
-总复杂度:$\mathcal{O}(mT+n\frac{m}{T})$。
+总复杂度:$O(mT+n\frac{m}{T})$。
 
-$T=\sqrt{n}$ 时,总复杂度 $O(m \sqrt{n})$。
\ No newline at end of file
+$T=\sqrt{n}$ 时,总复杂度 $O(m \sqrt{n})$。
index db9db73..5004a13 100644 (file)
@@ -5,7 +5,7 @@
 <!-- more -->
 
 1. 要去刻画最优解的结构特征;
-2. 尝试递归地定义最优解的值(就是我们常说的考虑从 `i - 1` 转移到 `i`);
+2. 尝试递归地定义最优解的值(就是我们常说的考虑从 $i - 1$ 转移到 $i$);
 3. 计算最优解;
 4. 利用计算出的信息构造一个最优解。
 
@@ -84,9 +84,9 @@
 
 子序列允许不连续。
 
-每个 `c[i][j]` 只依赖于 `c[i - 1][j]`、`c[i][j - 1]` 和 `c[i - 1][j - 1]`
+每个 $c[i][j]$ 只依赖于 $c[i - 1][j]$、$c[i][j - 1]$ 和 $c[i - 1][j - 1]$
 
-记录最优方案的时候可以不需要额外建表(优化空间),因为重新选择一遍(转移过程)也是 $\mathcal{O}(1)$ 的。
+记录最优方案的时候可以不需要额外建表(优化空间),因为重新选择一遍(转移过程)也是 $O(1)$ 的。
 
 ## 最优二叉搜索树
 
@@ -97,7 +97,7 @@
 由于每个节点的深度都增加了1,这棵子树的期望搜索代价的增加值应为所有概率之和。
 
 > tD / eD 动态规划:
-状态空间是 $\mathcal{O}(n^t)$ 的,每一项依赖其他 $\mathcal{O}(n^e)$ 项。
+状态空间是 $O(n^t)$ 的,每一项依赖其他 $O(n^e)$ 项。
 
 ## 经典问题(来自习题)
 
@@ -110,7 +110,7 @@ $dp[i] = \max(dp[j] + 1), ((j, i) \in E)$
 $$
 dp[i][i + len] =
 \begin{cases}
-dp[i + 1][i + len - 1] + 2,  & \text{if $s[i] == s[i + len]$} \\[2ex]
+dp[i + 1][i + len - 1] + 2,  & \text{if $s[i] = s[i + len]$} \\[2ex]
 \max(dp[i + 1][i + len], dp[i][i + len - 1]), & \text{else}
 \end{cases}
 $$
@@ -127,9 +127,9 @@ $$
 
 ### 最长回文子串
 
-$\mathcal{O}(n^2)$:$dp[i] = \max(dp[j] + 1), s(j + 1 .. i) 是回文$
+$O(n^2)$:$dp[i] = \max(dp[j] + 1), s(j + 1 .. i)$ 是回文
 
-$\mathcal{O}(n)$: Manacher
+$O(n)$: Manacher
 
 > Manacher
 
@@ -141,15 +141,15 @@ $p[i]$ 表示从 $i$ 向两侧延伸(当然要保证两侧对应位置相等
 
 考虑如果按顺序得到了 $p[1 .. i - 1]$,如何计算 $p[i]$ 的值?
 
-如果之前有一个位置比如说是 `id`,有 `p[id] + id > i` 那么 `i` 这个位置是被覆盖了的,根据 `id` 处的对称性,我们找 `p[id * 2 - i]` 延伸的部分被 `p[id]` 延伸的部分所覆盖的那段,显然这段对称回去之后是可以从 `i` 处延伸出去的长度。
+如果之前有一个位置比如说是 $id$,有 $p[id] + id > i$ 那么 $i$ 这个位置是被覆盖了的,根据 $id$ 处的对称性,我们找 $p[id * 2 - i]$ 延伸的部分被 $p[id]$ 延伸的部分所覆盖的那段,显然这段对称回去之后是可以从 $i$ 处延伸出去的长度。
 
-如果找不到呢?就先让 `p[i] = 1` 吧。
+如果找不到呢?就先让 $p[i] = 1$ 吧。
 
 之后再暴力延伸一下。
 
-可以证明是 $\mathcal{O}(n)$ 的。
+可以证明是 $O(n)$ 的。
 
-至于如何找是否有这么一个 `id` 呢?递推的时候存一个 `max` 就好了。
+至于如何找是否有这么一个 $id$ 呢?递推的时候存一个 $max$ 就好了。
 
 代码在:https://github.com/Ir1d/Fantasy/blob/master/HDU/3068.cpp
 
@@ -196,9 +196,9 @@ $dp[i][j] = \min(dp[i - 1][j] + dis[j][i]),\ j = 1..i$
 
 这样的转移更好写:
 
-我们记 `k = max(i, j) + 1`
+我们记 $k = \max(i, j) + 1$
 
-`k` 这个点肯定在两条路中的一个上,$dp[i][j]$ 取两种情况的最小值即可。
+$k$ 这个点肯定在两条路中的一个上,$dp[i][j]$ 取两种情况的最小值即可。
 
 $dp[i][j] = \min(dp[i][k] + dis[k][j], dp[k][j] + dis[i][k])$
 
@@ -214,7 +214,7 @@ $dp[i][j] = \min(dp[i][k] + dis[k][j], dp[k][j] + dis[i][k])$
 
 `## 论不要把题目看复杂`
 
-$dp[i] = min(dp[j] + cost[j][i])$
+$dp[i] = \min(dp[j] + cost[j][i])$
 
 ----
 
@@ -248,7 +248,7 @@ insert  : -2
 
 没有上司的舞会。
 
-`dp[x][0]` 是没去,`dp[x][1]` 是去了。
+$dp[x][0]$ 是没去,$dp[x][1]$ 是去了。
 
 $dp[u][0] = \max(dp[v][0], dp[v][1]), v \in son(u)$
 
@@ -283,7 +283,7 @@ $dp[i][j] = \min(dp[i - 1][j], dp[i - 1][j - 1], dp[i - 1][j + 1]) + cost[i][j
 
 $dp[i][j] = \min(dp[i][k] + dp[k][j]) + l[j] - l[i] + 1,\ k = i + 1\ ..\ j - 1$
 
-注意`l[i]`表示的是第i个切分点的位置。
+注意 $l[i]$ 表示的是第i个切分点的位置。
 
 边界:$dp[i][i] = 0$。
 
@@ -317,4 +317,4 @@ https://walkccc.github.io/CLRS/Chap15/Problems/15-12/
 
 ----
 
-当选取的状态难以进行递推时(分解出的子问题和原问题形式不一样),考虑将问题状态分类细化,增加维度。
\ No newline at end of file
+当选取的状态难以进行递推时(分解出的子问题和原问题形式不一样),考虑将问题状态分类细化,增加维度。
index 099c5a4..21855aa 100644 (file)
@@ -4,23 +4,23 @@
 
 什么意思呢?我们开一个数组,数组里每个元素是图的一条边。
 
-这样做有个缺点,每次想要知道两个点之间是否有连边(或者说一条边是否存在),都需要在数组里进行一番查找。而且如果没有对边事先排序的话,就不能使用二分查找的方法($\mathcal{O}(\log n)$),而是每次只能按顺序找($\mathcal{O}(n)$),成本较高。
+这样做有个缺点,每次想要知道两个点之间是否有连边(或者说一条边是否存在),都需要在数组里进行一番查找。而且如果没有对边事先排序的话,就不能使用二分查找的方法($O(\log n)$),而是每次只能按顺序找($O(n)$),成本较高。
 
 什么时候会用到这个方法呢?最简单的一个例子是使用 Kruskal 算法求 [最小生成树](graph/mst) 的时候。
 
 ### 邻接矩阵
 
-邻接矩阵的英文名是 adjacency matrix。它的形式是 `bool adj[n][n]`,这里面 `n` 是节点个数,`adj[i][j]` 表示 i 和 j 之间是否有边。
+邻接矩阵的英文名是 adjacency matrix。它的形式是 `bool adj[n][n]`,这里面 $n$ 是节点个数,$adj[i][j]$ 表示 $i$ 和 $j$ 之间是否有边。
 
 如果边有权值,也可以直接用 `int adj[n][n]`,直接把边权存进去。
 
-它的优点是可以在 $\mathcal{O}(1)$ 时间内得到一条边是否存在,缺点是需要占用 $\mathcal{O}(n^2)$ 的空间。对于一个稀疏的图(边相对于点数的平方比较少)来说,用邻接矩阵来存的话,成本偏高。
+它的优点是可以在 $O(1)$ 时间内得到一条边是否存在,缺点是需要占用 $O(n^2)$ 的空间。对于一个稀疏的图(边相对于点数的平方比较少)来说,用邻接矩阵来存的话,成本偏高。
 
 ### 邻接表
 
 邻接表英文名是 adjacency list。它的形式是 `vector adj[n]`,用 `adj[i]` 存以 $i$ 为起点的边。
 
-用 vector 无法科学地删除,所以常用 list 实现。
+用 `vector` 无法科学地删除,所以常用 `list` 实现。
 
 它的特点是可以用来按顺序访问一个结点的出边(或者入边)。
 
@@ -36,7 +36,7 @@
 
 其中 `head[i]` 用来存以 $i$ 为起点的边,`edge` 数组是边表。
 
-那么什么是前向星呢?事先把 `edge` 数组排个序即可。这里可以使用 [基数排序](basic/sort) 做到 $\mathcal{O}(m)$。
+那么什么是前向星呢?事先把 `edge` 数组排个序即可。这里可以使用 [基数排序](basic/sort) 做到 $O(m)$。
 
 ## 一些跟图有关的定义
 
@@ -60,23 +60,23 @@ cycle,也称为 `环`,是起点和终点相同的路径。
 
 #### 两个点连通
 
-无向图中点 `u` 和 `v` 连通是指存在一条 `u` 到 `v` 的路径。
+无向图中点 $u$ 和 $v$ 连通是指存在一条 $u$ 到 $v$ 的路径。
 
 #### 图连通
 
-如果无向图 G 中任意两个节点连通,称其为是连通的。
+如果无向图 $G$ 中任意两个节点连通,称其为是连通的。
 
 ### 可达
 
-有向图中点 `u` 到 `v` 可达是指存在一条 `u` 到 `v` 的路径。
+有向图中点 $u$ 到 $v$ 可达是指存在一条 $u$ 到 $v$ 的路径。
 
 ### [强连通](/graph/scc)
 
-有向图 G 强连通是指,G 中任意两个节点连通。
+有向图 $G$ 强连通是指,$G$ 中任意两个节点连通。
 
 ### [弱连通](/graph/bcc)
 
-有向图 G 弱连通是指,G 中的所有边替换为无向边后,G 为连通图。
+有向图 $G$ 弱连通是指,$G$ 中的所有边替换为无向边后,$G$ 为连通图。
 
 ### 子图
 
@@ -106,15 +106,15 @@ cycle,也称为 `环`,是起点和终点相同的路径。
 
 ### 稀疏图
 
-$m = \Theta(n)$ 的图,或者指 `m` 相对较小的图。
+$m = \Theta(n)$ 的图,或者指 $m$ 相对较小的图。
 
 ### 稠密图
 
-$m = \Theta(n^2)$ 的图,或者指 `m` 相对较大的图。
+$m = \Theta(n^2)$ 的图,或者指 $m$ 相对较大的图。
 
 ### 完全图
 
-$m = n(n-1) / 2$ 的简单无向图。
+$m = \frac{n(n-1)}{2}$ 的简单无向图。
 
 ### 路径的长度
 
index f93fc5b..f4bdefc 100644 (file)
@@ -52,9 +52,9 @@ for (edge(u, v, len) in sorted(edges)) {
 }
 ```
 
-`find_set` 调用 $\mathcal{O}(m)$ 次,merge 调用 $\mathcal{O}(n)$ 次。
+`find_set` 调用 $O(m)$ 次,merge 调用 $O(n)$ 次。
 
-排序的复杂度为 $\mathcal{O}(m \log m)$,或 $\mathcal{O}(m)$(假设能基数排序)。
+排序的复杂度为 $O(m \log m)$,或 $O(m)$(假设能基数排序)。
 
 ### “集合”数据结构的一种实现
 
@@ -62,15 +62,15 @@ for (edge(u, v, len) in sorted(edges)) {
 
 我们先考虑暴力,直接维护每个元素属于哪个集合,以及每个集合有哪些元素。
 
-find_set:$\mathcal{O}(1)$
+find_set:$O(1)$
 
-merge:$\mathcal{O}(n)$,需要将一个集合中的所有元素移到另一个集合中。
+merge:$O(n)$,需要将一个集合中的所有元素移到另一个集合中。
 
 于是考虑如何优化 merge。
 
 一个简单的思路是,将较小的集合中所有元素移到较大的集合中。
 
-复杂度是 $\mathcal{O}(较小集合的大小)$。
+复杂度是 $O(较小集合的大小)$。
 
 那么总时间复杂度是多少呢?
 
@@ -80,11 +80,11 @@ merge:$\mathcal{O}(n)$,需要将一个集合中的所有元素移到另一
 
 所以一个元素所在的集合,最多有 $\log n$ 次,作为较小集合被合并。
 
-一共$n$个元素,所以总时间复杂度为 $\mathcal{O}(n \log n + m)$。
+一共$n$个元素,所以总时间复杂度为 $O(n \log n + m)$。
 
 这种做法或者思想,叫「启发式合并」。
 
-总之我们得到了 $\mathcal{O}(n \log n + m \log m)$ 的 Kruskal 算法。
+总之我们得到了 $O(n \log n + m \log m)$ 的 Kruskal 算法。
 
 ## Prim 算法
 
@@ -127,11 +127,11 @@ merge:$\mathcal{O}(n)$,需要将一个集合中的所有元素移到另一
 
 其实跟 Dijkstra 算法一样,只要一个堆来维护距离即可。
 
-暴力:$\mathcal{O}(n^2+m)$。
+暴力:$O(n^2+m)$。
 
-二叉堆:$\mathcal{O}((n+m) \log n)$。
+二叉堆:$O((n+m) \log n)$。
 
-Fib 堆:$\mathcal{O}(n \log n + m)$。
+Fib 堆:$O(n \log n + m)$。
 
 (伪代码)
 ```
@@ -168,4 +168,4 @@ Kruskal 算法中的「集合」,能否进一步优化?
 
 ## 次小生成树
 
-## 第 k 小生成树
\ No newline at end of file
+## 第 k 小生成树
index 2b5691e..3c49455 100644 (file)
@@ -3,11 +3,14 @@
 这种数据结构看起来像是一个倒挂的树,因此得名。
 
 形式化的定义:
-- 有 $n$ 个节点, $n-1$ 条边的连通无向图,
+
+- 有 $n$ 个节点, $n-1$ 条边的连通无向图
+
 - 无向无环连通图
+
 - 任意两个节点之间有且仅有一条简单路径的无向图
 
-## 有关树的定义s
+## 有关树的定义
 
 ### 森林
 
@@ -46,7 +49,7 @@
 
 ### 子节点
 
-如果 u 是 v 的父亲,那么 v 是 u 的子节点。
+如果 $u$ 是 $v$ 的父亲,那么 $v$ 是 $u$ 的子节点。
 
 ### 兄弟
 
@@ -56,7 +59,7 @@
 
 子节点和子节点的后代
 
-或者理解成:如果 u 是 v 的祖先,那么 v 是 u 的后代。
+或者理解成:如果 $u$ 是 $v$ 的祖先,那么 $v$ 是 $u$ 的后代。
 
 ### 度数
 
 
 #### 无根树
 
-度数不超过 1 的节点
+度数不超过 $1$ 的节点
 
-??? question "为什么不是度为 1?"
+??? question "为什么不是度为 $1$?"
     考虑 $n = 1$ 的时候。
 
 #### 有根树
 
-没有子节点(度数为 0)的节点
+没有子节点(度数为 $0$)的节点
 
 ### 子树
 
@@ -103,7 +106,7 @@ $n = 1, m = 0$
 
 所有非根节点的父亲均为根节点。
 
-输的深度为 1.
+输的深度为 $1$。
 
 ### 二叉树
 
index 9c14c85..8ff1772 100644 (file)
 
 ###排列的定义
 
-从$n$个不同元素中,任取$m$($m≤n$,$m$与$n$均为自然数,下同)个元素按照一定的顺序排成一列,叫做从$n$个不同元素中取出$m$个元素的一个排列;从$n$个不同元素中取出$m$($m≤n$)个元素的所有排列的个数,叫做从$n$个不同元素中取出$m$个元素的排列数,用符号 $A_n^m$ 表示。
+从 $n$ 个不同元素中,任取 $m$($m≤n$,$m$ 与 $n$ 均为自然数,下同)个元素按照一定的顺序排成一列,叫做从 $n$ 个不同元素中取出 $m$ 个元素的一个排列;从 $n$ 个不同元素中取出 $m$($m≤n$) 个元素的所有排列的个数,叫做从 $n$ 个不同元素中取出 $m$ 个元素的排列数,用符号 $A_n^m$ 表示。
 
 ###排列的计算公式
 
-$$A_n^m = n(n-1)(n-2)......(n-m+1) = \frac{n!}{(n - m)!}$$
+$$A_n^m = n(n-1)(n-2) \cdots (n-m+1) = \frac{n!}{(n - m)!}$$
 
-**$n$!代表$n$的阶乘,即6! = 1 × 2 × 3 × 4 × 5 × 6.**
+**$n!$ 代表 $n$ 的阶乘,即$6! = 1 \times 2 \times 3 \times 4 \times 5 \times 6$.**
 
 ###组合的定义
 
-从$n$个不同元素中,任取$m$($m≤n$)个元素并成一组,叫做从$n$个不同元素中取出$m$个元素的一个组合;从$n$个不同元素中取出$m$($m≤n$)个元素的所有组合的个数,叫做从$n$个不同元素中取出$m$个元素的组合数。用符号 $C_n^m$ 表示。
+从 $n$ 个不同元素中,任取 $m$($m≤n$)个元素并成一组,叫做从 $n$ 个不同元素中取出 $m$ 个元素的一个组合;从 $n$ 个不同元素中取出 $m$($m≤n$)个元素的所有组合的个数,叫做从 $n$ 个不同元素中取出 $m$ 个元素的组合数。用符号 $C_n^m$ 表示。
 
 ###组合的计算公式
 
@@ -33,20 +33,20 @@ $$C_n^m = \frac{A_n^m}{m!} = \frac{n!}{m!(n - m)!}$$
 ###排列
 
 **全排列**:<br>
-$n$个人全部来排队,队长为$n$。第一个位置可以选$n$个,第二位置可以选$n-1$个,以此类推得:
+$n$个人全部来排队,队长为 $n$。第一个位置可以选 $n$ 个,第二位置可以选 $n-1$ 个,以此类推得:
 
-$$A_n^n = n(n-1)(n-2)......3 × 2 × 1 = n!$$
+$$A_n^n = n(n-1)(n-2) \cdots 3 × 2 × 1 = n!$$
 
 **部分排列**:<br>
-$n$个人选$m$个来排队($m<=n$)。第一个位置可以选$n$个,第二位置可以选$n-1$个,以此类推,第$m$个(最后一个)可以选($n-m+1$)个,得:
+$n$ 个人选 $m$ 个来排队($m \le n$)。第一个位置可以选 $n$ 个,第二位置可以选 $n-1$ 个,以此类推,第 $m$ 个(最后一个)可以选 $n-m+1$ 个,得:
 
-$$A_n^m = n(n-1)(n-2)......(n-m+1) = \frac{n!}{(n - m)!}$$
+$$A_n^m = n(n-1)(n-2) \cdots (n-m+1) = \frac{n!}{(n - m)!}$$
 
 ###组合
 
-$n$个人$m$($m<=n$)个出来,不排队,不在乎顺序$C_n^m$。如果在乎排列那么就是$A_n^m$,如果不在乎那么就要除掉重复,那么重复了多少?同样选出的来的$m$个人,他们还要“全排”得$A_n^m$,所以得:
+$n$ 个人 $m$($m \le n$) 个出来,不排队,不在乎顺序 $C_n^m$。如果在乎排列那么就是 $A_n^m$,如果不在乎那么就要除掉重复,那么重复了多少?同样选出的来的 $m$ 个人,他们还要“全排”得 $A_n^m$,所以得:
 
-$$C_n^m * m! = A_n,m$$
+$$C_n^m \times m! = A_n,m$$
 
 $$C_n^m = \frac{A_n^m}{m!} = \frac{n!}{m!(n-m)!}$$
 
@@ -60,53 +60,53 @@ $$C_n^m = C_{n-1}^{m} + C_{n-1}^{m-1}$$
 
 ###圆排列
 
-n个人全部来围成一圈为$Q_n^n$,其中已经排好的一圈,从不同位置断开,又变成不同的队列。<br>
+n个人全部来围成一圈为 $Q_n^n$,其中已经排好的一圈,从不同位置断开,又变成不同的队列。<br>
 所以:
 
-$$Q_n^n * n = A_n^n → Q_n = \frac{A_n^n}{n} = (n-1)!$$
+$$Q_n^n \times n = A_n^n → Q_n = \frac{A_n^n}{n} = (n-1)!$$
 
 **由此可知:部分圆排**
 
-$$Q_n^r = \frac{A_n^r}{r} = \frac{n!}{r * (n-r)!}$$
+$$Q_n^r = \frac{A_n^r}{r} = \frac{n!}{r \times (n-r)!}$$
 
 ###重复排列(有限)
 
-$k$种不一样的球,每种球的个数分别是a_1,a_2,...a_k,设$n=a_1+a_2+…+a_k$,这$n$个球的全排列数,为
+$k$种不一样的球,每种球的个数分别是 $a_1,a_2,\cots,a_k$,设 $n=a_1+a_2+…+a_k$,这 $n$ 个球的全排列数,为
 
-$$\frac{n!}{a_1! * a_2! * ... * a_k!}$$
+$$\frac{n!}{a_1! \times a_2! \times \cots \times a_k!}$$
 
 ###重复组合(无限)
-$n$种不一样的球,每种球的个数是无限的,从中选$k$个出来,不用排列,是组合,为$C_{n+k-1}^{k}$.
+$n$ 种不一样的球,每种球的个数是无限的,从中选$k$个出来,不用排列,是组合,为$C_{n+k-1}^{k}$.
 
 证明:
 >假设选出来的数(排好序):
 
->$$1<=b_1<=b_2<=b_3<=...<=b_k<=n$$
+>$$1 \le b_1 \le b_2 \le b_3 \le \cots \le b_k \le n$$
 
->这题的难点就是$=$号,现在去掉$=$号,所以有:
+>这题的难点就是 $=$ 号,现在去掉 $=$ 号,所以有:
 
->$$1<=b_1<b_2+1<b_3+2<b_4+3<...<b_k+k-1<=n+k-1$$
+>$$1 \le b_1<b_2+1<b_3+2<b_4+3<\cots<b_k+k-1 \le n+k-1$$
 
-中间还是$k$个数!不过已经不是$b$系列,而是$c$系列,**假设$c[i]=b[i]+i-1$,所以**
+中间还是 $k$ 个数!不过已经不是 $b$ 系列,而是 $c$ 系列,**假设 $c[i]=b[i]+i-1$,所以**
 
-$$1<=c_1<c_2<c_3<c_4<...<c_k<=n+k-1$$
+$$1 \le c_1<c_2<c_3<c_4<\cots<c_k \le n+k-1$$
 
-所以问题就开始转换为无重复组合问题,即在$n+k-1$个元素中选中$k$个的组合数$C_{n+k-1}^{k}$。
+所以问题就开始转换为无重复组合问题,即在 $n+k-1$ 个元素中选中 $k$ 个的组合数$C_{n+k-1}^{k}$。
 
 ###不相邻的排列
 
-$1$~$n$这$n$个自然数中选$k$个,这$k$个数中任何两个数不相邻数的组合有$C_{n-k+1}^{k}$。<br>
+$1 \sim n$ 这 $n$ 个自然数中选 $k$ 个,这 $k$ 个数中任何两个数不相邻数的组合有 $C_{n-k+1}^{k}$种。<br>
 证明和上面的相同(其实就是懒得写),请自行证明XD
 
 ###错位排列(错排)
 
 先看一个小问题:<br>
-5本书,编号分别是$1,2,3,4,5$,现在要把这5本书是放在编号$1,2,3,4,5$的书架上,要求书的编号和书架的编号不一样,请问有多少种不一样的放置方法?
+$5$ 本书,编号分别是 $1,2,3,4,5$,现在要把这5本书是放在编号 $1,2,3,4,5$ 的书架上,要求书的编号和书架的编号不一样,请问有多少种不一样的放置方法?
 
 再看一个小问题:<br>
-胸口贴着编号为$1,2,...,n$的$n$个球员分别住在编号为$1,2,...,n$的$n$个房间里面。现规定每个人住一个房间,自己的编号不能和房间的编号一样。
+胸口贴着编号为 $1,2,\cots,n$ 的 $n$ 个球员分别住在编号为 $1,2,\cots,n$ 的 $n$ 个房间里面。现规定每个人住一个房间,自己的编号不能和房间的编号一样。
 
-这就是错排问题。当$n=3$时,只能为312或231这两种。
+这就是错排问题。当 $n=3$ 时,只能为 312 或 231 这两种。
 
 那么错排问题的解题思路是什么呢?我们以第二个问题为例:
 **递推还是王道!!!**
@@ -114,12 +114,12 @@ $1$~$n$这$n$个自然数中选$k$个,这$k$个数中任何两个数不相邻
 刚开始所有球员都住在和自己编号一样的房间里面。然后错排开始了,第$n$个球员从第出来。
 
 第一种情况:<br>
-$n$想和$i(1<=i<=n-1)$其中任何一个球员换房间,其他$n-2$个人换房间的事情,他们就不管了。其他$n-2$个球员的的错排数为$d[n-2]$,$n$可以和前面1~$(n-1)$对换,所以有$(n-1)$个$d[n-2]$。
+$n$ 想和 $i(1 \le i \le n-1)$ 其中任何一个球员换房间,其他 $n-2$ 个人换房间的事情,他们就不管了。其他 $n-2$ 个球员的的错排数为 $d[n-2]$,$n$ 可以和前面 $1 \sim n-1$ 对换,所以有 $n-1$ 个 $d[n-2]$。
 
 第二种情况:<br>
-$n$想和$i(1<=i<=n-1)$其中任何一个球员换房间,但是$n$只想$i$住在第$N$个房间,而$n$不想住第$I$个房间。
+$n$ 想和 $i(1 \le i \le n-1)$ 其中任何一个球员换房间,但是 $n$ 只想 $i$ 住在第 $N$ 个房间,而 $n$ 不想住第 $I$ 个房间。
 
-可能你会这样想:那么$n$可以让$j$住在第$I$号房间里面,然后$n$住在房间$J$。抱歉,$j(1<=j<=n-1,j\neq i)$生气$n$为什么一开始就去找$i$不直接来找$j$。没办法,$n$把自己胸口的编码$N$换成了$I$,他假装自己是$i$,然后错排$1$~$n-1$(也就是 $d[n-2]$)的时候参与进去,这样自己就不会呆在第$I$号房间了。所以有$n-1$个$d[n-1]$。
+可能你会这样想:那么 $n$ 可以让 $j$ 住在第 $I$ 号房间里面,然后 $n$ 住在房间 $J$。抱歉,$j(1 \le j \le n-1,j\neq i)$生气 $n$ 为什么一开始就去找 $i$ 不直接来找 $j$。没办法,$n$ 把自己胸口的编码 $N$ 换成了 $I$,他假装自己是 $i$,然后错排 $1 \sim n-1$(也就是 $d[n-2]$)的时候参与进去,这样自己就不会呆在第 $I$ 号房间了。所以有 $n-1$ 个 $d[n-1]$。
 
 如果理解了以上内容,那么错排的公式就出来了:
 
@@ -129,19 +129,19 @@ $$d_n = (n-1)(d_{n-2} + d_{n-1}) (n\geq3)$$
 
 $$d_n = n * d_{n-1} + (-1)^n$$
 
-错位排列数列为$0,1,2,9,44,265,...$
+错位排列数列为 $0,1,2,9,44,265,\cots$
 
 ##加法&乘法原理
 
 ###加法原理
 
-完成一个工程可以有$n$类办法,$a[i](1<=i<=n)$ 代表第$i$类方法的数目。<br>
-那么完成这件事共有$S=a[1]+a[2]+...+a[n]$种不同的方法。
+完成一个工程可以有 $n$ 类办法,$a[i](1 \le i \le n)$ 代表第 $i$ 类方法的数目。<br>
+那么完成这件事共有 $S=a[1]+a[2]+\cots+a[n]$ 种不同的方法。
 
 ###乘法原理
 
-完成一个工程需要分$n$个步骤,$a[i](1<=i<=n)$ 代表第$i$个步骤的不同方法数目。<br>
-那么完成这件事共有$S = a[1] * a[2] * ... * a[n]$种不同的方法。
+完成一个工程需要分 $n$ 个步骤,$a[i](1 \le i \le n)$ 代表第 $i$ 个步骤的不同方法数目。<br>
+那么完成这件事共有$S = a[1] \times a[2] \times \cots \times a[n]$ 种不同的方法。
 
 ###两原理的区别
 
@@ -149,7 +149,7 @@ $$d_n = n * d_{n-1} + (-1)^n$$
 
 ##几个关于组合的公式
 
-$$C_n^0 + C_n^1 + C_n^2 + C_n^3 + ... + C_n^m = 2^n$$
+$$C_n^0 + C_n^1 + C_n^2 + C_n^3 + \cots + C_n^m = 2^n$$
 
 $$C_n^r + C_n^{r+1} = C_{n+1}^{r+1}$$
 
index 64a392f..aadbe43 100644 (file)
@@ -31,7 +31,7 @@ $$
 
 1. 计算所有模数的积 $n$;
 2. 对于第 $i$ 个方程:
-       1. 计算 $m_i=n/n_i$;
+       1. 计算 $m_i=\frac{n}{n_i}$;
        2. 计算 $m_i$ 在模 $n_i$ 意义下的[逆元](/math/inverse/) $m_i^{-1}$;
        3. 计算 $c_i=m_im_i^{-1}$(**不要对 $n_i$ 取模**)。
 3. 方程组的唯一解为:$a=\sum_{i=1}^k a_ic_i \pmod n$。
index 4c7b9be..817a2d2 100644 (file)
@@ -1,4 +1,4 @@
-欧拉函数是什么? $\phi(n)$ 表示的是小于等于 n 和 n 互质的数的个数。
+欧拉函数是什么? $\phi(n)$ 表示的是小于等于 $n$ 和 $n$ 互质的数的个数。
 
 比如说 $\phi(1) = 1$。
 
@@ -6,19 +6,19 @@
 
 利用唯一分解定理,我们可以把一个整数唯一地分解为质数幂次的乘积,
 
-设 $n = p_1^{k_1}p_2^{k_2} \cdots p_s^{k_s}$,其中 $p_i$ 是质数,那么定义 $\phi(n) = n * \prod_{i = 1}^s{\frac{p_i - 1}{p_i}}$
+设 $n = p_1^{k_1}p_2^{k_2} \cdots p_s^{k_s}$,其中 $p_i$ 是质数,那么定义 $\phi(n) = n \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}}$
 
 ### 欧拉函数的一些神奇性质
 
 - 欧拉函数是积性函数
-积性是什么意思呢?如果有 $gcd(a, b) = 1$,那么 $\phi(a * b) = \phi(a) * \phi(b)$
-特别地,当 $n$ 是奇数时 $\phi(2n) = 2 * \phi(n)$
+积性是什么意思呢?如果有 $\gcd(a, b) = 1$,那么 $\phi(a \times b) = \phi(a) \times \phi(b)$
+特别地,当 $n$ 是奇数时 $\phi(2n) = 2 \times \phi(n)$
 
 - $n = \sum_{d | n}{\phi(d)}$
 利用 [莫比乌斯反演](./mobius/) 相关知识可以得出
-也可以这样考虑:如果 $gcd(k, n) = d$,那么 $gcd(k / d, n / d) = 1$。($k < n$)
-如果我们设 $f(x)$ 表示 $gcd(k, n) = x$ 的数的个数,那么 $n = \sum_{i = 1}^n{f(x)}$。
-根据上面的证明,我们发现,$f(x) = phi(n / x)$,从而 $n = \sum_{d | n}\phi(n / d)$。注意到约数 $d$ 和 $n / d$ 具有对称性,所以上式化为 $n = \sum_{d | n}\phi(d)$
+也可以这样考虑:如果 $\gcd(k, n) = d$,那么 $\gcd(\frac{k}{d},\frac{n}{d}) = 1$。($k < n$)
+如果我们设 $f(x)$ 表示 $\gcd(k, n) = x$ 的数的个数,那么 $n = \sum_{i = 1}^n{f(x)}$。
+根据上面的证明,我们发现,$f(x) = \phi(\frac{n}{x})$,从而 $n = \sum_{d | n}\phi(\frac{n}{d})$。注意到约数 $d$ 和 $\frac{n}{d}$ 具有对称性,所以上式化为 $n = \sum_{d | n}\phi(d)$
 
 - 若 $n = p^k$,其中 $p$ 是质数,那么 $\phi(n) = p^k - p^{k - 1}$。
 (根据定义可知)
@@ -27,4 +27,4 @@
 
 如果只要求一个数的欧拉函数值,那么直接根据定义质因数分解的同时求就好了
 
-如果是多个数的欧拉函数值,可以利用后面会提到的线性筛法来求得。
\ No newline at end of file
+如果是多个数的欧拉函数值,可以利用后面会提到的线性筛法来求得。
index aafc7e0..815c537 100644 (file)
@@ -14,7 +14,7 @@ NTT 部分代码参考 CSDN 上的模板代码附网址,感谢博主!
 
 ## DFT IDFT FFT 官方定义?
 > 离散傅里叶变换(Discrete Fourier Transform,缩写为 DFT),是傅里叶变换在时域和频域上都呈离散的形式,将信号的时域采样变换为其 DTFT 的频域采样。
-> 
+>
 > FFT 是一种 DFT 的高效算法,称为快速傅立叶变换(fast Fourier transform)。 ——百度百科
 
 在百度百科上能找到 DFT 和 FFT 这两个定义。正如定义,FFT 和 DFT 实际上按照结果来看的话是一样的,但是 FFT 比较快的计算 DFT 和IDFT(离散反傅里叶变换)。
@@ -29,9 +29,9 @@ NTT 部分代码参考 CSDN 上的模板代码附网址,感谢博主!
 
 大家平时求 $f(x)=a_1x^2+b_1x+c_1$ 与 $g(x) = a_2x^2+b_2x+c_2$ 的乘积时候,是怎么进行的呢?
 
-我们令 
+我们令
 
-$$K(x) = f(x) * g(x) = a_1x^2*a_2x^2+a_1x^2*b_2x+a_1x^2*c_2+b_1x*b_2x^2+b_1x*b_2x+b_1x*c_2+c_1*a_2x^2+c_1*b_2x+c_1*c_2$$
+$$K(x) = f(x)  \times  g(x) = a_1x^2 \times a_2x^2+a_1x^2 \times b_2x+a_1x^2 \times c_2+b_1x \times b_2x^2+b_1x \times b_2x+b_1x \times c_2+c_1 \times a_2x^2+c_1 \times b_2x+c_1 \times c_2$$
 
 那么很显然我们进行了 9 次运算,复杂度是 $O(n^2)$ (具体代码实现不再展开)
 
@@ -50,7 +50,7 @@ FFT,即为快速傅氏变换,是离散傅氏变换的快速算法,它是
 
 $$f(x) = a_1x^2+b_1x+c_1 \Leftrightarrow f(x) = \{a_1, b_1, c_1\}$$
 
-点值表示法是把这个多项式看成一个函数,从上面选取 n+1 个点,从而利用这 n+1 个点来唯一的表示这个函数。为什么用(n+1)个点就能唯一的表示这个函数了呢?想一下高斯消元法,两点确定一条直线。再来一个点,能确定这个直线中的另一个参数,那么也就是说(n+1)个点能确定 n 个参数(不考虑倍数点之类的没用点)。如下:
+点值表示法是把这个多项式看成一个函数,从上面选取 $n+1$ 个点,从而利用这 $n+1$ 个点来唯一的表示这个函数。为什么用 $n+1$ 个点就能唯一的表示这个函数了呢?想一下高斯消元法,两点确定一条直线。再来一个点,能确定这个直线中的另一个参数,那么也就是说 $n+1$ 个点能确定 $n$ 个参数(不考虑倍数点之类的没用点)。如下:
 
 $$f_1(x) = y_1 = a_0 + a_1x_1+a_2x_1^2+a_3x_1^3+ \cdots + a_nx_1^n$$
 
@@ -73,17 +73,17 @@ $$f_n(x) = y_n6 = a_0 + a_1x_m+a_2x_m^2+a_3x_m^3+ \cdots + a_nx_m^n$$
 
 相对地,把一个多项式的点值表示法转化为系数表示法的过程,就是 IDFT。
 
-而 FFT 就是通过取某些特殊的 x 的点值来加速 DFT 和 FFT 的过程。
+而 FFT 就是通过取某些特殊的 $x$ 的点值来加速 DFT 和 FFT 的过程。
 
 ### 复数的引入
-复数分为实数和虚数。实数就是我们日常最常用的有理数和无理数。大家记得我们在开始学平方的时候,老师会说所有数的平方大于等于 0 对不对,那么虚数就引入了。虚数一般用 i 表示,对于虚数 i,有 $i=\sqrt{-1}$
+复数分为实数和虚数。实数就是我们日常最常用的有理数和无理数。大家记得我们在开始学平方的时候,老师会说所有数的平方大于等于 $0$ 对不对,那么虚数就引入了。虚数一般用 $i$ 表示,对于虚数 $i$,有 $i=\sqrt{-1}$
 
 
-。另外,i 对于虚数的意义,与 1 对于实数的意义是一样的。如果我说得不够明确,你可以看下面我引用的百科说明。
+。另外,$i$ 对于虚数的意义,与 $1$ 对于实数的意义是一样的。如果我说得不够明确,你可以看下面我引用的百科说明。
 
-> 在数学中,虚数就是形如 $a+b*i$ 的数,其中 a,b 是实数,且 $b \neq 0$,$i^2 = - 1$。虚数这个名词是 17 世纪著名数学家笛卡尔创立,因为当时的观念认为这是真实不存在的数字。后来发现虚数 a+b*i 的实部 a 可对应平面上的横轴,虚部 b 与对应平面上的纵轴,这样虚数 a+b*i 可与平面内的点 (a,b) 对应。
+> 在数学中,虚数就是形如 $a+b \times i$ 的数,其中 $a,b$ 是实数,且 $b \neq 0$,$i^2 = - 1$。虚数这个名词是 17 世纪著名数学家笛卡尔创立,因为当时的观念认为这是真实不存在的数字。后来发现虚数 $a+b \times i$ 的实部 $a$ 可对应平面上的横轴,虚部 $b$ 与对应平面上的纵轴,这样虚数 $a+b \times i$ 可与平面内的点 $(a,b)$ 对应。
 
-> 可以将虚数 bi 添加到实数 a 以形成形式 a + bi 的复数,其中实数 a 和 b 分别被称为复数的实部和虚部。一些作者使用术语纯虚数来表示所谓的虚数,虚数表示具有非零虚部的任何复数。 ——百度百科 
+> 可以将虚数 $bi$ 添加到实数 $a$ 以形成形式 $a + bi$ 的复数,其中实数 $a$ 和 $b$ 分别被称为复数的实部和虚部。一些作者使用术语纯虚数来表示所谓的虚数,虚数表示具有非零虚部的任何复数。 ——百度百科
 
 我们用一幅图来表示复数与复平面的关系(图源百度百科)
 
@@ -94,11 +94,11 @@ $$f_n(x) = y_n6 = a_0 + a_1x_m+a_2x_m^2+a_3x_m^3+ \cdots + a_nx_m^n$$
 
 接下来思考两个复数相乘是什么意义:
 
-1. $(a+bi)*(c+di) = (ac-bd) + (ad+bc)i$
+1. $(a+bi) \times (c+di) = (ac-bd) + (ad+bc)i$
 
-2. 长度相乘,角度相加: $(r_1, \theta_1) * (r_2, \theta_2) = (r_1*r_2, \theta_1+\theta_2)$
+2. 长度相乘,角度相加: $(r_1, \theta_1)  \times  (r_2, \theta_2) = (r_1 \times r_2, \theta_1+\theta_2)$
 
-这么一看的话,我们很容易想到如果两个长度为 1 的不同方向向量相乘,结果向量是不是一个长度依然为 1 的新向量呢?
+这么一看的话,我们很容易想到如果两个长度为 $1$ 的不同方向向量相乘,结果向量是不是一个长度依然为 $1$ 的新向量呢?
 
 ### 单位复根的引入
 
@@ -106,38 +106,38 @@ $$f_n(x) = y_n6 = a_0 + a_1x_m+a_2x_m^2+a_3x_m^3+ \cdots + a_nx_m^n$$
 
 考虑这样一个问题:
 
-刚刚说到了 DFT 是把多项式从系数表示转到了点值表示(复杂度为 O(n)),那么我们把点值相乘之后(选取相应位置,并且复杂度为 O(n)),如果能够快速还原成系数表示,是不是就完美解决我们的问题了呢?上述过程如下:
+刚刚说到了 DFT 是把多项式从系数表示转到了点值表示(复杂度为 $O(n)$),那么我们把点值相乘之后(选取相应位置,并且复杂度为 $O(n)$),如果能够快速还原成系数表示,是不是就完美解决我们的问题了呢?上述过程如下:
 
-假设我们 DFT 过程对于两个多项式选取的 x 序列相同,那么可以得到
+假设我们 DFT 过程对于两个多项式选取的 $x$ 序列相同,那么可以得到
 
 $$f(x)={(x_0, f(x_0), (x_1, f(x_1)), (x_2, f(x_2), \cdots, (x_n, f(x_n)))}$$
 
 $$g(x)={(x_0, g(x_0), (x_1, g(x_1)), (x_2, g(x_2), \cdots, (x_n, g(x_n)))}$$
 
-如果我们设 $F(x) = f(x)*g(x0$
+如果我们设 $F(x) = f(x) \times g(x0$
 
 那么很容易得到 $F(x)$ 的点值表达式:
 
-$$F(x) = {(x_0, f(x_0) * g(x_0), (x_1, f(x_1) * g(x_1)), (x_2, f(x_2) * g(x_2), \cdots, (x_n, f(x_n) * g(x_n)))}$$
+$$F(x) = {(x_0, f(x_0)  \times  g(x_0), (x_1, f(x_1)  \times  g(x_1)), (x_2, f(x_2)  \times  g(x_2), \cdots, (x_n, f(x_n)  \times  g(x_n)))}$$
 
-但是我们要的是系数表达式,接下来问题变成了从点值回到系数。如果我们带入到高斯消元法的方程组中去,会把复杂度变得非常高。光是计算 $x^i(0 \leq i \leq n)$ 就是 n 项, 这就已经 $O(n^2)$ 了,更别说还要把(n+1)个方程进行消元。。。。。。。。。
+但是我们要的是系数表达式,接下来问题变成了从点值回到系数。如果我们带入到高斯消元法的方程组中去,会把复杂度变得非常高。光是计算 $x^i(0 \leq i \leq n)$ 就是 $n$ 项, 这就已经 $O(n^2)$ 了,更别说还要把 $n+1$ 个方程进行消元。。。。。。。。。
 
-这里会不会觉得我们不去计算 $x^i$ 比较好呢?1 和 - 1 的幂都很好算,但是也仅仅有两个不够啊,我们至少需要(n+1)个 o(╥﹏╥)o 那怎么办呢!想到我们刚刚学的长度为 1 的虚数了吗?不管怎么乘长度都是 1!对就是它!我们需要的是 $\omega^k=1$ 中的 $\omega$,很容易想到 - i 和 1 是符合的。那其他的呢?
+这里会不会觉得我们不去计算 $x^i$ 比较好呢?$1$ 和 $-1$ 的幂都很好算,但是也仅仅有两个不够啊,我们至少需要 $n+1$ 个 o(╥﹏╥)o 那怎么办呢!想到我们刚刚学的长度为 $1$ 的虚数了吗?不管怎么乘长度都是 $1$!对就是它!我们需要的是 $\omega^k=1$ 中的 $\omega$,很容易想到 $-i$ 和 $1$ 是符合的。那其他的呢?
 
 ![img](https://pic4.zhimg.com/80/v2-04ee497b8ac007ce8fff9ce9a04373fb_hd.jpg)
 
-现在我们看上图的圆圈。容易发现这是一个单位圆(圆心为原点,半径为 1),所有在圆上的复数的长度均为 1,也就是说它不管做多少次方 r 永远为 1,结果也仅仅角度的变化而已。但是!!!进过旋转总会让角度 %360 == 0 成立的,也就是结果为 1. 我们把符合以上条件的复数成为复根,用 $\omega$ 表示。如果 $\omega^k=1$ 那么我们把$\omega$ 称为 1 的 k 次复根,记作 $\omega_k^n$ (因为符合这个 k 次之后等于 1 的复数有很多,比如 i 的 4k 次幂永远为 1,所以,这个 n 是一个编号,表示这是角度从小到大的第几个(从 x 的正半轴开始逆时针))
+现在我们看上图的圆圈。容易发现这是一个单位圆(圆心为原点,半径为 $1$),所有在圆上的复数的长度均为 $1$,也就是说它不管做多少次方 $r$ 永远为 $1$,结果也仅仅角度的变化而已。但是!!!进过旋转总会让角度 $\bmod 360 = 0$ 成立的,也就是结果为 $1$。 我们把符合以上条件的复数成为复根,用 $\omega$ 表示。如果 $\omega^k=1$ 那么我们把$\omega$ 称为 $1$ 的 $k$ 次复根,记作 $\omega_k^n$ (因为符合这个 $k$ 次之后等于 $1$ 的复数有很多,比如 $i$ 的 $4k$ 次幂永远为 $1$,所以,这个 $n$ 是一个编号,表示这是角度从小到大的第几个(从 $x$ 的正半轴开始逆时针))
 
 是不是有点雾啊( ̄▽ ̄)/没事没事接下来我们举个栗子:
 
 ![img](https://pic4.zhimg.com/80/v2-9f81f6f445b2420d3871b1b024db5197_hd.jpg)
 
-那么很容易发现当 K = 4 的时候,相当于把单位圆等分 K= 4 份。然后每一份按照极角编号。那么是不是(在 K = 4 的时候)我们只要知道 $\omega_4^1$
+那么很容易发现当 $K = 4$ 的时候,相当于把单位圆等分 $K= 4$ 份。然后每一份按照极角编号。那么是不是(在 $K = 4$ 的时候)我们只要知道 $\omega_4^1$
 
 
 (因为他的角度是相当于单位角度), 就能知道 $\omega_4^0, \omega_4^1, \omega_4^2, \omega_4^3$ 了呢?当然是这样的。。。
 
-$\omega_4^0$ 恒等于 1,$\omega_4^2$ 的角度是 $\omega_4^0$ 的两倍,所以 $\omega_4^2 = (\omega_4^1)^2 = i^2=-1$, 依次以此类推。
+$\omega_4^0$ 恒等于 $1$,$\omega_4^2$ 的角度是 $\omega_4^0$ 的两倍,所以 $\omega_4^2 = (\omega_4^1)^2 = i^2=-1$, 依次以此类推。
 
 因此,我们只要知道 $\omega_k^1$ ,就能求出 $\omega_k^n$。所以我们把 $\omega_k^1$ 称为单位复根,简写为 $\omega_k$
 
@@ -153,11 +153,11 @@ FFT 之所以快,是因为他采用了分治的思想。
 
 的时候整个式子的值。他的分治思想体现在将多项式分为奇次项和偶次项处理。
 
-对于一共 8 项的多项式
+对于一共 $8$ 项的多项式
 
 $$f(x0) = y_1 = a_0 + a_1x + a_2x^2+a_3x^3+a_4x^4+a_5x^5+a_6x^6+a_7x^7$$
 
-按照次数的奇偶来分成两组, 然后右边提出来一个 x
+按照次数的奇偶来分成两组, 然后右边提出来一个 $x$
 
 $$f(x) = (a_0+a_2x^2+a_4x^4+a_6x^6) + (a_1x+a_3x^3+a_5x^5+a_7x^7)$$
 
@@ -169,27 +169,27 @@ $$G(x) = a_0+a_2x+a_4x^2+a_6x^3$$
 
 $$H(x)=a_1+a_3x+a_5x^2+a_7x^3$$
 
-那么原来的 f(x)由新函数来表示(是不是我们二分了一个多项式呢~)
+那么原来的 $f(x)$ 由新函数来表示(是不是我们二分了一个多项式呢~)
 
-$$F(x)=G(x^2) + x * H(x^2)$$
+$$F(x)=G(x^2) + x  \times  H(x^2)$$
 
 给函数带个帽子表示此时在进行的是 DFT 过程,把 x 代进去,即有
 
-$$DFT(f(\omega_n^k))=DFT(G((\omega_n^k)^2)) + \omega_n^k * DFT(H((\omega_n^k)^2))$$
+$$DFT(f(\omega_n^k))=DFT(G((\omega_n^k)^2)) + \omega_n^k  \times  DFT(H((\omega_n^k)^2))$$
 
 !!!前方高能:
 
-这个函数能处理的多项式长度只能是 $2^m(m \in N^*)$ , 否则在分治的时候左右不一样长,右边取不到系数了,程序没法进行。所以要在第一次 DFT 之前就把序列向上补成长度为 $2^m(m \in N^*)$(高次系数补 0)、最高项次数为(n-1)的多项式。一定要预处理哦 qaq
+这个函数能处理的多项式长度只能是 $2^m(m \in N^ \times )$ , 否则在分治的时候左右不一样长,右边取不到系数了,程序没法进行。所以要在第一次 DFT 之前就把序列向上补成长度为 $2^m(m \in N^ \times )$(高次系数补 $0$)、最高项次数为 $n-1$ 的多项式。一定要预处理哦 qaq
 
-然后我在代入值的时候,因为要代入 n 个不同值,所以我们就代入 $\omega_n^0,\omega_n^1,\omega_n^2,\cdots, \omega_n^{n-1} (n=2^m(m \in N^*))$
+然后我在代入值的时候,因为要代入 $n$ 个不同值,所以我们就代入 $\omega_n^0,\omega_n^1,\omega_n^2,\cdots, \omega_n^{n-1} (n=2^m(m \in N^ \times ))$
 
 一共 $2^m$ 个不同值。
 
 ```c++
 /*
-* 做 FFT 
-*len 必须是 2^k 形式 
-*on == 1 时是 DFT,on == -1 时是 IDFT 
+* 做 FFT
+*len 必须是 2^k 形式
+*on == 1 时是 DFT,on == -1 时是 IDFT
 */
 void fft(Complex y[],int len){
        change(y,len);
@@ -224,10 +224,10 @@ void fft(Complex y[],int len){
 这里附上代码
 ```c++
 /*
-* 进行 FFT 和 IFFT 前的反置变换 
-* 位置 i 和 i 的二进制反转后的位置互换 
-*len 必须为 2 的幂 
-*/ 
+* 进行 FFT 和 IFFT 前的反置变换
+* 位置 i 和 i 的二进制反转后的位置互换
+*len 必须为 2 的幂
+*/
 void change(Complex y[],int len){
        int i,j,k;
        for(int i = 1,j = len/2;i<len-1;i++){
@@ -238,7 +238,7 @@ void change(Complex y[],int len){
                while(j >= k){
                        j = j - k;
                        k = k/2;
-               } 
+               }
                if(j < k)       j+=k;
        }
 }
@@ -250,28 +250,28 @@ void change(Complex y[],int len){
 
 ![img](https://pic4.zhimg.com/80/v2-74756f9edea1e47bb965a9371a478a92_hd.jpg)
 
-而且现在我们已经得到最左边的结果了,中间的 x 值在目标多项式的点值表示中也是一一对应的,所以,根据矩阵的基础知识,我们只要在式子两边左乘中间那个大矩阵的逆矩阵就行了。由于这个矩阵的元素非常特殊,他的逆矩阵也有特殊的性质,就是每一项取倒数,再除以 n,就能得到他的逆矩阵(这边根据的是单位原根的两个特殊性质推出来的,具体比较麻烦。如果想知道的话私我吧。)
+而且现在我们已经得到最左边的结果了,中间的 $x$ 值在目标多项式的点值表示中也是一一对应的,所以,根据矩阵的基础知识,我们只要在式子两边左乘中间那个大矩阵的逆矩阵就行了。由于这个矩阵的元素非常特殊,他的逆矩阵也有特殊的性质,就是每一项取倒数,再除以 $n$,就能得到他的逆矩阵(这边根据的是单位原根的两个特殊性质推出来的,具体比较麻烦。如果想知道的话私我吧。)
 
-如何改变我们的操作才能使计算的结果文原来的倒数呢?我们当然可以重新写一遍,但是这里有更简单的实现。这就要看我们求 “单位复根的过程了”:根据“欧拉函数”$e^{i\pi}=-1$,我么可以得到 $e^{2\pi i}=1$。如果我要找到一个数,它的 k 次方 = 1,那么这个数$\omega[k]=e^{2\pi i/k}$(因为$(e^{2\pi i/k})^k=e^{2\pi i}=1$)。而如果我要使这个数值变成“$1/\omega[k]$” 也就是 “$(\omega[k])^-1$”,我们可以尝试着把π取成 - 3.14159…,这样我们的计算结果就会变成原来的倒数,而其它的操作过程与 DFT 是完全相同的(这真是极好的)。我们可以定义一个函数,向里面掺一个参数“1” 或者是 “-1”,然后把它乘到“π” 的身上。传入 “1” 就是 DFT,传入 “-1” 就是 IDFT,十分的智能。
+如何改变我们的操作才能使计算的结果文原来的倒数呢?我们当然可以重新写一遍,但是这里有更简单的实现。这就要看我们求 “单位复根的过程了”:根据“欧拉函数” $e^{i\pi}=-1$,我么可以得到 $e^{2\pi i}=1$。如果我要找到一个数,它的 $k$ 次方 $= 1$,那么这个数 $\omega[k]=e^{2\pi \frac{i}{k}}$(因为 $(e^{2\pi \frac{i}{k}})^k=e^{2\pi i}=1$)。而如果我要使这个数值变成$\frac{1}{\omega[k]}$ 也就是 $(\omega[k])^-1$,我们可以尝试着把$$π$ 取成 - 3.14159…,这样我们的计算结果就会变成原来的倒数,而其它的操作过程与 DFT 是完全相同的(这真是极好的)。我们可以定义一个函数,向里面掺一个参数 $1$ 或者是 $-1$,然后把它乘到 $π$ 的身上。传入 $1$ 就是 DFT,传入 $-1$ 就是 IDFT,十分的智能。
 
 所以我们 fft 函数可以集 DFT 和 IDFT 于一身。见下
 
 ```c++
 /*
-* 做 FFT 
-*len 必须是 2^k 形式 
-*on == 1 时是 DFT,on == -1 时是 IDFT 
+* 做 FFT
+*len 必须是 2^k 形式
+*on == 1 时是 DFT,on == -1 时是 IDFT
 */
 void fft(Complex y[],int len,int on){
        change(y,len);
-       for(int h = 2;h <= len;h<<=1){// 模拟合并过程 
+       for(int h = 2;h <= len;h<<=1){// 模拟合并过程
                Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));// 计算当前单位复根
                for(int j = 0;j < len;j += h){
                        Complex w(1,0);// 计算当前单位复根
                        for(int k = j;k < j + h/2;k++){
                                Complex u = y[k];
                                Complex t = w*y[k + h/2];
-                               y[k] = u + t;// 这就是吧两部分分治的结果加起来 
+                               y[k] = u + t;// 这就是吧两部分分治的结果加起来
                                y[k + h/2] = u - t;
                                // 后半个 “step” 中的ω一定和 “前半个” 中的成相反数
                 //“红圈”上的点转一整圈“转回来”,转半圈正好转成相反数
@@ -315,10 +315,10 @@ struct Complex{
        }
 };
 /*
-* 进行 FFT 和 IFFT 前的反置变换 
-* 位置 i 和 i 的二进制反转后的位置互换 
-*len 必须为 2 的幂 
-*/ 
+* 进行 FFT 和 IFFT 前的反置变换
+* 位置 i 和 i 的二进制反转后的位置互换
+*len 必须为 2 的幂
+*/
 void change(Complex y[],int len){
        int i,j,k;
        for(int i = 1,j = len/2;i<len-1;i++){
@@ -329,14 +329,14 @@ void change(Complex y[],int len){
                while(j >= k){
                        j = j - k;
                        k = k/2;
-               } 
+               }
                if(j < k)       j+=k;
        }
-} 
+}
 /*
-* 做 FFT 
-*len 必须是 2^k 形式 
-*on == 1 时是 DFT,on == -1 时是 IDFT 
+* 做 FFT
+*len 必须是 2^k 形式
+*on == 1 时是 DFT,on == -1 时是 IDFT
 */
 void fft(Complex y[],int len,int on){
        change(y,len);
@@ -358,7 +358,7 @@ void fft(Complex y[],int len,int on){
                        y[i].x /= len;
                }
        }
-} 
+}
 
 const int MAXN = 200020;
 Complex x1[MAXN],x2[MAXN];
@@ -405,4 +405,4 @@ int main(){
 
 ## 算竞选手看过来~ NTT(数论优化的快速傅里叶变换)
 
-戳~ [NTT](/math/ntt)
\ No newline at end of file
+戳~ [NTT](/math/ntt)
index 0947d7f..211ad96 100644 (file)
@@ -6,16 +6,16 @@
 
 其实这个变换在信号处理中应用很广泛,fft 是 double 类型的,但是 walsh 把信号在不同震荡频率方波下拆解,因此所有的系数都是绝对值大小相同的整数,这使得不需要作浮点数的乘法运算,提高了运算速度。
 
-所以,FWT 和 FFT 的核心思想应该是相同的。都是对数组的变换。我们设数组 A 经过快速沃尔什变换之后记作 $FWT[A]$.
+所以,FWT 和 FFT 的核心思想应该是相同的。都是对数组的变换。我们设数组 $A$ 经过快速沃尔什变换之后记作 $FWT[A]$.
 
 那么 FWT 核心思想就是:
 
-我们需要一个新序列 C,由序列 A 和序列 B 经过某运算规则得到,即 $C = A * B$
+我们需要一个新序列 $C$,由序列 $A$ 和序列 $B$ 经过某运算规则得到,即 $C = A \cdot B$
 
 
 我们先正向得到 $FWT[A], FWT[B]$
 
-然后根据 $FWT[C]=FWT[A]*FWT[B]$ (这里的 $*$ 是点乘)
+然后根据 $FWT[C]=FWT[A] \cdot FWT[B]$
 
 在 $O(n)$ 求出 $FWT[C]$
 
 
 ### 或运算 $A_i$
 
-如果有 $k=i|j$,那么 i 的二进制位为 1 的位置和 j 的二进制位为 1 的位置肯定是 k 的二进制位为 1 的位置的子集。
+如果有 $k=i|j$,那么 $i$ 的二进制位为 $1$ 的位置和 $j$ 的二进制位为 $1$ 的位置肯定是 $k$ 的二进制位为 $1$ 的位置的子集。
 
 现在要得到 $FWT[C] = FWT[A] * FWT[B]$,我们就要构造这个 fwt 的规则。
 
-我们按照定义,显然可以构造 $FWT[A] = A' = \sum_{i=i|j}A[j]$,来表示 j 满足二进制中 1 为 i 的子集。
+我们按照定义,显然可以构造 $FWT[A] = A' = \sum_{i=i|j}A[j]$,来表示 $j$ 满足二进制中 $1$ 为 $i$ 的子集。
 
 那么显然会有 $C[i] = \sum_{i=j|k}A[j]*B[k] \Rightarrow FWT[C] = FWT[A] * FWT[B]$
 
@@ -49,7 +49,7 @@
 
 我们把整个区间二分,其实二分区间之后,下标写成二进制形式是有规律可循的。
 
-我们令 $A_0$ 表示 A 的前一半,$A_1$ 表示区间的后一半,那么 $A_0$ 就是 A 下标最大值的最高位为 0,他的子集就是他本身的子集(因为最高位为 0 了),但是 $A_1$ 的最高位是 1,他满足条件的子集不仅仅是他本身,还包最高位为 0 的子集,即
+我们令 $A_0$ 表示 $A$ 的前一半,$A_1$ 表示区间的后一半,那么 $A_0$ 就是 A 下标最大值的最高位为 $0$,他的子集就是他本身的子集(因为最高位为 $0$ 了),但是 $A_1$ 的最高位是 $1$,他满足条件的子集不仅仅是他本身,还包最高位为 $0$ 的子集,即
 
 $$FWT[A] = merge(FWT[A_0], FWT[A_0] + FWT[A_1])$$
 
@@ -75,13 +75,13 @@ $$UFWT[A'] = merge(UFWT[A_0'] - UFWT[A_1'], UFWT[A_1'])$$
 
 异或的卷积是基于如下原理:
 
-若我们令 $i\And j$ 中 1 数量的奇偶性为 i 与 j 的奇偶性,那么 i 与 k 的奇偶性异或 j 和 k 的奇偶性等于 i^j 和 k 的奇偶性。
+若我们令 $i\And j$ 中 $1$ 数量的奇偶性为 $i$ 与 $j$ 的奇偶性,那么 $i$ 与 $k$ 的奇偶性异或 $j$ 和 $k$ 的奇偶性等于 $i \operatorname{xor} j$ 和 $k$ 的奇偶性。
 
 对于 $FWT[A]$ 的运算其实也很好得到。
 
 公式如下:
 
-$A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ($C_1$ 表示 $i \And j$ 奇偶性为 0,$C_2$ 表示 $i \And j$ 的奇偶性为 1)
+$A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ($C_1$ 表示 $i \And j$ 奇偶性为 $0$,$C_2$ 表示 $i \And j$ 的奇偶性为 $1$)
 
 结论:
 
@@ -93,7 +93,7 @@ $$UFWT[A'] - merge(\frac{FWT[A_0'] + FWT[A_1']}{2}, \frac{FWT[A_0'] - FWT[A_1']}
 
 类比异或运算给出公式:
 
-$A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ($C_1$ 表示 $i|j$ 奇偶性为 0,$C_2$ 表示 $i|j$ 的奇偶性为 1)
+$A[i] = \sum_{C_1}A[j] - \sum_{C_2}A[j]$ ($C_1$ 表示 $i|j$ 奇偶性为 $0$,$C_2$ 表示 $i|j$ 的奇偶性为 $1$)
 
 $$FWT[A] = merge(FWT[A_1] - FWT[A_0], FWT[A_1] + FWT[A_0])$$
 
index fb63d24..2458c57 100644 (file)
 
 ##消元法及高斯消元法思想
 
-###1.1消元法说明
+###1.1 消元法说明
 
 消元法是将方程组中的一方程的未知数用含有另一未知数的代数式表示,并将其带入到另一方程中,这就消去了一未知数,得到一解;或将方程组中的一方程倍乘某个常数加到另外一方程中去,也可达到消去一未知数的母的。消元法主要用于二元一次方程组的求解。
 
 例一:利用消元法求解二元一次线性方程组:
 
 $\left\{\begin{aligned}
-4x+y&=100 \notag \\ 
+4x+y&=100 \notag \\
 x-y&=100 \notag
 \end{aligned}\right.$
 
-解:将方程组中两方程相加,消元y可得:
+解:将方程组中两方程相加,消元 $y$ 可得:
 
 $5x = 200$
 
@@ -29,54 +29,66 @@ $5x = 200$
 
 $x = 40$
 
-将$x$ = 40代入方程组中第二个方程可得:
+将 $x = 40$ 代入方程组中第二个方程可得:
 
 $y = -60$
 
 
 ###1.2  消元法理论的核心
 
-消元法理论的核心主要如下:<br>
-* 两方程互换,解不变;<br>
-* 一方程乘以非零数$k$,解不变;<br>
-* 一方程乘以数$k$加上另一方程,解不变。<br>
+消元法理论的核心主要如下:
+
+* 两方程互换,解不变;
+
+* 一方程乘以非零数 $k$,解不变;
+
+* 一方程乘以数 $k$ 加上另一方程,解不变。
 
 
 ###1.3  高斯消元法思想概念
 
-德国数学家高斯对消元法进行了思考分析,得出了如下结论:<br>
-* 在消元法中,参与计算和发生改变的是方程中各变量的系数;<br>
-* 各变量并未参与计算,且没有发生改变;<br>
-* 可以利用系数的位置表示变量,从而省略变量;<br>
-* 在计算中将变量简化省略,方程的解不变。<br>
+德国数学家高斯对消元法进行了思考分析,得出了如下结论:
+
+* 在消元法中,参与计算和发生改变的是方程中各变量的系数;
+
+* 各变量并未参与计算,且没有发生改变;
+
+* 可以利用系数的位置表示变量,从而省略变量;
+
+* 在计算中将变量简化省略,方程的解不变。
 
 >高斯在这些结论的基础上,提出了高斯消元法,首先将方程的增广矩阵利用行初等变换化为行最简形,然后以线性无关为准则对自由未知量赋值,最后列出表达方程组通解。
 
 
 ##高斯消元五步骤法
 
-高斯消元法在将增广矩阵化为最简形后对于自由未知量的赋值,需要掌握线性相关知识,且赋值存在人工经验的因素,使得在学习过程中有一定的困难,将高斯消元法划分为五步骤,从而提出五步骤法,内容如下:<br>
-* Step1 增广矩阵行初等行变换为行最简形;<br>
-* Step2 还原线性方程组;<br>
-* Step3 求解第一个变量;<br>
-* Step4 补充自由未知量;<br>
-* Step5 列表示方程组通解。<br>
+高斯消元法在将增广矩阵化为最简形后对于自由未知量的赋值,需要掌握线性相关知识,且赋值存在人工经验的因素,使得在学习过程中有一定的困难,将高斯消元法划分为五步骤,从而提出五步骤法,内容如下:
+
+1. 增广矩阵行初等行变换为行最简形;
+
+2. 还原线性方程组;
+
+3. 求解第一个变量;
+
+4. 补充自由未知量;
+
+5. 列表示方程组通解。
 
 利用实例进一步说明该算法的运作情况。
 
 例二:利用高斯消元法五步骤法求解线性方程组:
 
 $\left\{\begin{aligned}
-2x_1+5x_3+6x_4&=9 \notag \\ 
+2x_1+5x_3+6x_4&=9 \notag \\
 x_3+x_4&=-4 \notag \\
 2x_3+2x_4&=-8 \notag
 \end{aligned}\right.$
 
 ###第1步  增广矩阵行(初等)变换为行最简形
 
-所谓增广矩阵,即为方程组系数矩阵A与常数列b的并生成的新矩阵,即(A | b),增广矩阵行初等变换化为行最简形,即是利用了高斯消元法的思想理念,省略了变量而用变量的系数位置表示变量,增广矩阵中用竖线隔开了系数矩阵和常数列,代表了等于符号。
+所谓增广矩阵,即为方程组系数矩阵 $A$ 与常数列 $b$ 的并生成的新矩阵,即 $(A | b)$,增广矩阵行初等变换化为行最简形,即是利用了高斯消元法的思想理念,省略了变量而用变量的系数位置表示变量,增广矩阵中用竖线隔开了系数矩阵和常数列,代表了等于符号。
 
-$\left(\begin{matrix} 
+$\left(\begin{matrix}
 2 & 0 & 5 & 6 \\
 0 & 0 & 1 & 1 \\
 0 & 0 & 2 & 2
@@ -84,11 +96,11 @@ $\left(\begin{matrix}
 \begin{matrix}
 9 \\
 -4 \\
--8 
+-8
 \end{matrix} \right)$
 
 $\xrightarrow{r_3-2r_2}
-\left(\begin{matrix} 
+\left(\begin{matrix}
 2 & 0 & 5 & 6 \\
 0 & 0 & 1 & 1 \\
 0 & 0 & 0 & 0
@@ -99,10 +111,10 @@ $\xrightarrow{r_3-2r_2}
 0
 \end{matrix} \right)$
 
-化为行阶梯形 
+化为行阶梯形
 
 $\xrightarrow{\frac{r_1}{2}}
-\left(\begin{matrix} 
+\left(\begin{matrix}
 1 & 0 & 2.5 & 3 \\
 0 & 0 & 1 & 1 \\
 0 & 0 & 0 & 0
@@ -114,7 +126,7 @@ $\xrightarrow{\frac{r_1}{2}}
 \end{matrix} \right)$
 
 $\xrightarrow{r_1-r_2 \times 2.5}
-\left(\begin{matrix} 
+\left(\begin{matrix}
 1 & 0 & 0 & 0.5 \\
 0 & 0 & 1 & 1 \\
 0 & 0 & 0 & 0
@@ -130,37 +142,37 @@ $\xrightarrow{r_1-r_2 \times 2.5}
 ###第2步  还原线性方程组
 
 $\left\{\begin{aligned}
-x_1+0.5x_4 &= 14.5 \notag\\ 
+x_1+0.5x_4 &= 14.5 \notag\\
 x_3+x_4 &= -4 \notag \\
 \end{aligned}\right.$
-解释  
+
+解释
 
 >所谓的还原线性方程组,即是在行最简形的基础上,将之重新书写为线性方程组的形式,即将行最简形中各位置的系数重新赋予变量,中间的竖线还原为等号。
 
 ###第3步  求解第一个变量
 
 $\left\{\begin{aligned}
-x_1 &= -0.5x_4+14.5\notag \\ 
-x_3 &= -x_4-4\notag 
+x_1 &= -0.5x_4+14.5\notag \\
+x_3 &= -x_4-4\notag
 \end{aligned}\right.$
-解释  
 
->即是对于所还原的线性方程组而言,将方程组中每个方程的第一个变量,用其他量表达出来。如方程组两方程中的第一个变量$x_1$和$x_3$
+解释
+
+>即是对于所还原的线性方程组而言,将方程组中每个方程的第一个变量,用其他量表达出来。如方程组两方程中的第一个变量 $x_1$ 和 $x_3$
 
 ###第4步  补充自由未知量
 
 $\left\{\begin{aligned}
-x_1 &= -0.5x_4+14.5 \notag \\ 
+x_1 &= -0.5x_4+14.5 \notag \\
 x_2 &= x_2 \notag \\
 x_3 &= -x_4-4 \notag \\
 x_4 &= x_4 \notag
 \end{aligned}\right.$
-解释  
 
->第3步中,求解出变量$x_1$和$x_3$,从而说明了方程剩余的变量$x_2$和$x_4$不受方程组的约束,是自由未知量,可以取任意值,所以需要在第3步骤解得基础上进行解得补充,补充的方法为$x_2$ = $x_2$、$x_4$ = $x_4$,这种解得补充方式符合自由未知量定义,并易于理解,因为是自由未知量而不受约束,所以只能自己等于自己。
+解释
+
+>第3步中,求解出变量 $x_1$ 和 $x_3$,从而说明了方程剩余的变量 $x_2$ 和 $ x_4$ 不受方程组的约束,是自由未知量,可以取任意值,所以需要在第 3 步骤解得基础上进行解得补充,补充的方法为 $x_2 = x_2,x_4 = x_4$,这种解得补充方式符合自由未知量定义,并易于理解,因为是自由未知量而不受约束,所以只能自己等于自己。
 
 ###第5步  列表示方程组的通解
 
@@ -173,18 +185,18 @@ $\begin{aligned}
 \begin{pmatrix} -0.5 \\ 0 \\ -1 \\ 1 \end{pmatrix} C_2 +
 \begin{pmatrix} 14.5 \\ 0 \\ -4 \\ 0 \end{pmatrix} \notag
 \end{aligned}$
-其中$C_1$和$C_2$为任意常数。
 
-解释  
+其中 $C_1$ 和 $C_2$ 为任意常数。
+
+解释
 
->即在第4步的基础上,将解表达为列向量组合的表示形式,同时由于$x_2$和$x_4$是自由未知量,可以取任意值,所以在解得右边,令二者分别为任意常数$C_1$和$C_2$,即实现了对方程组的求解。
+>即在第4步的基础上,将解表达为列向量组合的表示形式,同时由于 $x_2$ 和 $x_4$ 是自由未知量,可以取任意值,所以在解得右边,令二者分别为任意常数 $C_1$ 和 $C_2$,即实现了对方程组的求解。
 
 ***
 
 #行列式
 
-$N \times N$方阵行列式可以理解为所有列向量所夹的几何体的有向体积
+$N \times N$ 方阵行列式可以理解为所有列向量所夹的几何体的有向体积
 
 例如:
 
@@ -195,18 +207,22 @@ $\begin{vmatrix}
 $\begin{vmatrix}
 1 & 2 \\
 2 & 1 \end{vmatrix} = -3$
+
 行列式有公式
 
 $D = \left| A \right| = \sum(-1)^va_{1,l_1}a_{2,l_2}\dots a_{n,l_n}$
->其中$v$为$l_1$, $l_2$,……, $l_n$中逆序对的个数。
 
-通过体积概念理解行列式不变性是一个非常简单的办法:<br>
-* 矩阵转置,行列式不变;<br>
-* 矩阵行交换,行列式取反;<br>
-* 矩阵行叠加,行列式不变;<br>
-* 矩阵行伸长,行列式等比例变大。<br>
+>其中 $v$ 为 $l_1$, $l_2$,\cdots, $l_n$ 中逆序对的个数。
+
+通过体积概念理解行列式不变性是一个非常简单的办法:
+
+* 矩阵转置,行列式不变;
+
+* 矩阵行交换,行列式取反;
+
+* 矩阵行叠加,行列式不变;
+
+* 矩阵行伸长,行列式等比例变大。
 
 >由此,发现高斯消元不改变矩阵行列式,且最终行列式等于倒三角矩阵的对角线乘积。
 
@@ -237,7 +253,7 @@ $\begin{vmatrix}
 1 & -2 & 1 \\
 0 & 1 & -2 \end{vmatrix} = 4$
 
-附一个冗长的复杂的令人难过的高斯消元与Matrix Tree计数代码:
+附一个冗长的复杂的令人难过的高斯消元与 Matrix Tree 计数代码:
 
 ```cpp
 #include<iostream>
index b69c424..eaa9d5c 100644 (file)
@@ -8,14 +8,14 @@
 
 ### 两个数的
 
-如果我们已知两个数 a 和 b,如何求出二者的最大公约数呢?
+如果我们已知两个数 $a$ 和 $b$,如何求出二者的最大公约数呢?
 
 不妨设 $a > b$
 
 我们发现如果 b 是 a 的约数,那么 b 就是二者的最大公约数。
-下面讨论不能整除的情况, $a = b * q + r$,其中 $r < b$。
+下面讨论不能整除的情况, $a = b \times q + r$,其中 $r < b$。
 
-不难证明,$gcd(a, b) == gcd(b, r)$,这里两个数的大小是不会增大的,我们得到了关于两个数的最大公约数的一个递归求法。
+不难证明,$\gcd(a, b) = \gcd(b, r)$,这里两个数的大小是不会增大的,我们得到了关于两个数的最大公约数的一个递归求法。
 
 ### 多个数的
 
 我们发现,对于 a 和 b 的情况,二者的最大公约数等于
 
 $$
-p_1^{k_{min\{a_1, b_1\}}}p_2^{k_{min\{a_2, b_2\}}} \cdots p_s^{k_{min\{a_s, b_s\}}}
+p_1^{k_{\min(a_1, b_1)}}p_2^{k_{\min(a_2, b_2)}} \cdots p_s^{k_{\min(a_s, b_s)}}
 $$
 
 最小公倍数等于
 
 $$
-p_1^{k_{max\{a_1, b_1\}}}p_2^{k_{max\{a_2, b_2\}}} \cdots p_s^{k_{max\{a_s, b_s\}}}
+p_1^{k_{\max(a_1, b_1)}}p_2^{k_{\max(a_2, b_2)}} \cdots p_s^{k_{\max(a_s, b_s)}}
 $$
 
-由于 $a + b = max\{a, b\} + min\{a, b\}$
+由于 $a + b = \max(a, b) + \min(a, b)$
 
-所以得到结论是 $gcd(a, b) * lcm(a, b) = a * b$
+所以得到结论是 $\gcd(a, b) \times \operatorname{lcm}(a, b) = a \times b$
 
 要求两个数的最小公倍数,先求出最大公约数即可。
 
 ### 多个数的
-
index 0a86ae7..94c6256 100644 (file)
@@ -10,63 +10,61 @@ NTT 解决的是多项式乘法带模数的情况,可以说有些受模数的
 
 ### 生成子群
 
-子群:群 (S,⊕), (S′,⊕), 满足 S′⊂S,则(S′,⊕) 是(S,⊕)的子群
+子群:群 $(S,⊕), (S′,⊕)$, 满足 $S′⊂S$,则 $(S′,⊕)$ 是 $(S,⊕)$ 的子群
 
-拉格朗日定理:|S′|∣|S | 证明需要用到陪集,得到陪集大小等于子群大小,每个陪集要么不想交要么相等,所有陪集的并是集合 S,那么显然成立。
+拉格朗日定理:$|S′|∣|S |$ 证明需要用到陪集,得到陪集大小等于子群大小,每个陪集要么不想交要么相等,所有陪集的并是集合 $S$,那么显然成立。
 
-生成子群:$a \in S$ ​的生成子群 $<a> = \{a^{(k)}, k \geq 1 \}$ ​,a 是 $< a >$ 的生成元
+生成子群:$a \in S$ ​的生成子群 $<a> = \{a^{(k)}, k \geq 1 \}$ ​,$a$ 是 $< a >$ 的生成元
 
-阶:群 S 中 a 的阶是满足 $a^r=e$
+阶:群 $S$ 中 $a$ 的阶是满足 $a^r=e$
 
-​的最小的 r, 符号 $ord(a)$ ​, 有 $ord(a) = |< a >|$​,显然成立。
+​的最小的 $r$, 符号 $ \operatorname{ord}(a)$ ​, 有 $ \operatorname{ord}(a) = |< a >|$​,显然成立。
 
-考虑群 $Z_n^*=\{[a], n \in Z_n : gcd(a, n) = 1\}, |Z_n^*| = \phi(n)$
+考虑群 $Z_n^ \times =\{[a], n \in Z_n : \gcd(a, n) = 1\}, |Z_n^ \times | = \phi(n)$
 
-阶就是满足 $a^r \equiv 1 (\bmod n)$ ​的最小的 $r, ord(a)=r$
+阶就是满足 $a^r \equiv 1 (\bmod n)$ ​的最小的 $r$,  $\operatorname{ord}(a)=r$
 
 ### [原根](/math/primitive-root)
 
-g 满足 $ord_n(g) = |Z_n^*| = \phi(n)$  ​,对于质数 p,也就是说 $g^i \bmod p, 0 \leq i < p$ ​结果互不相同.
+$g$ 满足 $ \operatorname{ord}_n(g) = |Z_n^ \times | = \phi(n)$  ​,对于质数 $p$,也就是说 $g^i \bmod p, 0 \leq i < p$ ​结果互不相同.
 
-模 n 有原根的充要条件 : $n = 2, 4, p^e, 2*p^e$
+模 $n$ 有原根的充要条件 : $n = 2, 4, p^e, 2 \times p^e$
 
 离散对数:$g^t \equiv a (\bmod n),ind_{n,g}{(a)}=t$
 
-​因为 g 是原根,所以 gt 每 $\phi(n)$ 是一个周期,可以取到 | Z∗n | 的所有元素
-对于 n 是质数时,就是得到 [1,n−1] 的所有数,就是 [0,n−2] 到[1,n−1]的映射
+​因为 $g$ 是原根,所以 $gt$ 每 $\phi(n)$ 是一个周期,可以取到 $| Z \times n |$ 的所有元素
+对于 $n$ 是质数时,就是得到 $[1,n−1]$ 的所有数,就是 $[0,n−2]$ 到 $[1,n−1]$ 的映射
 离散对数满足对数的相关性质,如
 
 求原根可以证明满足 $g^r \equiv 1(\bmod p)$
 
-​的最小的 r 一定是 p−1 的约数
-对于质数 p,质因子分解 p−1,若 $g^{(p-1)/pi} \neq 1 (\bmod p)$
+​的最小的 $r$ 一定是 $p−1$ 的约数
+对于质数 $p$,质因子分解 $p−1$,若 $g^{(p-1)/pi} \neq 1 (\bmod p)$
 
-​恒成立,g 为 p 的原根
+​恒成立,$g$ 为 $p$ 的原根
 
 ## NTT
 
-对于质数 $p=qn+1, (n=2^m)$ ​, 原根 g 满足 $g^{qn} \equiv 1 (\bmod p)$​, 将 $g_n=g^p(\bmod q)$ 看做 $\omega_n$ 的等价,择其满足相似的性质,比如 $g_n^n \equiv 1 (\bmod p), g_n^{n/2} \equiv -1 (\bmod p)$
+对于质数 $p=qn+1, (n=2^m)$ ​, 原根 $g$ 满足 $g^{qn} \equiv 1 (\bmod p)$​, 将 $g_n=g^p(\bmod q)$ 看做 $\omega_n$ 的等价,择其满足相似的性质,比如 $g_n^n \equiv 1 (\bmod p), g_n^{n/2} \equiv -1 (\bmod p)$
 
-然后因为这里涉及到数论变化,所以这里的 N(为了区分 FFT 中的 n,我们把这里的 n 称为 N)可以比 FFT 中的 n 大,但是只要把 $\frac{qN}{n}$
+然后因为这里涉及到数论变化,所以这里的 $N$(为了区分 FFT 中的 n,我们把这里的 n 称为 $N$)可以比 FFT 中的 n 大,但是只要把 $\frac{qN}{n}$
 
-看做这里的 q 就行了,能够避免大小问题。。。
+看做这里的 $q$ 就行了,能够避免大小问题。。。
 
 常见的有
 
-$$p = 1004535809 = 479*2^21, g=3$$
+$$p = 1004535809 = 479 \times 2^21, g=3$$
 
-$$p=998244353=2*17*2^23+1, g=3$$
+$$p=998244353=2 \times 17 \times 2^23+1, g=3$$
 
 就是 $g^{qn}$ 的等价 $e^{2\pi n}$
 
-迭代到长度 l 时 $g_l = g^{\frac{p-1}{l}}$, 或者 $\omega_n = g_l = g_N^{\frac{N}{l}} = g_N^{\frac{p-1}{l}}$
-
+迭代到长度 $l$ 时 $g_l = g^{\frac{p-1}{l}}$, 或者 $\omega_n = g_l = g_N^{\frac{N}{l}} = g_N^{\frac{p-1}{l}}$
 
 接下来放一个大数相乘的模板
 
 参考网址如下 https://blog.csdn.net/blackjack_/article/details/79346433
 
-
 ```c++
 #include<cmath>
 #include<ctime>
@@ -176,4 +174,4 @@ int main()
        puts("");
        return 0;
 }
-```
\ No newline at end of file
+```
index 84e50df..72e4e43 100644 (file)
@@ -1,6 +1,6 @@
-我们说,如果存在一个整数 $k$,使得 $a = kd$,则称 d 整除 a,记做 $d | a$,称 a 是 d 的倍数,如果 $d > 0$,称 d 是 a 的约数。特别地,任何整数都整除 0
+我们说,如果存在一个整数 $k$,使得 $a = kd$,则称 $d$ 整除 $a$,记做 $d | a$,称 $a$ 是 $d$ 的倍数,如果 $d > 0$,称 $d$ 是 $a$ 的约数。特别地,任何整数都整除 $0$
 
-显然数 a 可以被 1 和 a,如果除此之外 a 没有其他的约数,则称 a 是素数。任何一个大于 1 的整数如果不是素数,就称为是合数。一个合数是除了 1 和它自身以外还有别的约数的数。1 既不是合数也不是约数。
+显然大于 $1$ 的正整数 $a$ 可以被 $1$ 和 $a$整除,如果除此之外 $a$ 没有其他的约数,则称 $a$ 是素数,又称质数。任何一个大于 $1$ 的整数如果不是素数,也就是有其他约数,就称为是合数。$1$ 既不是合数也不是素数。
 
 ## 素数判定
 
@@ -19,11 +19,11 @@ bool isPrime(a) {
 
 这样做是十分稳妥了,但是真的有必要每个数都去判断吗?
 
-很容易发现这样一个事实:如果 x 是 a 的约数,那么 $a / x$ 也是 a 的约数。
+很容易发现这样一个事实:如果 $x$ 是 $a$ 的约数,那么 $a / x$ 也是 $a$ 的约数。
 
 这个结论告诉我们,对于每一对 $(x, a / x)$,只需要检验其中的一个就好了。为了方便起见,我们之考察每一对里面小的那个数。不难发现,所有这些较小数就是 $[1 .. \sqrt(a)]$ 这个区间里的数。
 
-由于 1 肯定是约数,所以不检验它。
+由于 $1$ 肯定是约数,所以不检验它。
 
 ```c++
 bool isPrime(a) {
@@ -36,7 +36,7 @@ bool isPrime(a) {
 
 Miller-Rabbin 算法是进阶的素数判定方法,具有比暴力做法更好的时间复杂度。但是代码复杂度较高,在比赛中使用较少。
 
-它的基本思想是不断地选取不超过 $n-1$ 的基 $b$,并检验是否每次都有 $b^{n-1} \bmod n = 1$
+它的基本思想是不断地选取不超过 $n-1$ 的基 $b$,并检验是否每次都有 $b^{n-1} \equiv 1 \pmod n$
 
 ```c++
 bool millerRabbin(int n) {
@@ -52,8 +52,8 @@ bool millerRabbin(int n) {
 
 ### 定义
 
-如果某个正整数 n 满足如下条件,则称为是反素数:
-  任何小于 n 的正数的约数个数都小于 n 的约数个数
+如果某个正整数 $n$ 满足如下条件,则称为是反素数:
+  任何小于 $n$ 的正数的约数个数都小于 $n$ 的约数个数
 
 注:注意区分 [emirp](https://en.wikipedia.org/wiki/Emirp),它是用来表示从后向前写读是素数的数。
 
@@ -69,28 +69,31 @@ bool millerRabbin(int n) {
 
 首先,既然要求因子数,我首先想到的就是素因子分解。把 n 分解成 $n=p_{1}^{k_{1}}p_{2}^{k_{2}} \cdots p_{n}^{k_{n}}$
 
-的形式,其中 p 是素数,k 为他的指数。这样的话总因子个数就是 $(k_1+1)*(k_2+1)*(k_3+1) \cdots * (k_n+1)$。
+的形式,其中 p 是素数,k 为他的指数。这样的话总因子个数就是 $(k_1+1) \times (k_2+1) \times (k_3+1) \cdots \times (k_n+1)$。
 
 但是显然质因子分解的复杂度是很高的,并且前一个数的结果不能被后面利用。所以要换个方法。
 
 我们来观察一下反素数的特点。
 
 1. 反素数肯定是从 2 开始的连续素数的幂次形式的乘积。
+
 2. 数值小的素数的幂次大于等于数值大的素数,即 $n=p_{1}^{k_{1}}p_{2}^{k_{2}} \cdots p_{n}^{k_{n}}$ 中,有 $k1 \geq k2 \geq k3 \geq \cdots \geq k_n$
 
 解释:
-1. 如果不是从 2 开始的连续素数,那么如果幂次不变,把素数变成数值更小的素数,那么此时因子个数不变,但是 n 的数值变小了。交换到从 2 开始的连续素数的时候 n 值最小。
-2. 如果数值小的素数的幂次小于数值大的素数的幂,那么如果把这两个素数交换位置(幂次不变),那么所得的 n 因子数量不变,但是 n 的值变小。
+
+1. 如果不是从 $2$ 开始的连续素数,那么如果幂次不变,把素数变成数值更小的素数,那么此时因子个数不变,但是 $n$ 的数值变小了。交换到从 $2$ 开始的连续素数的时候 $n$ 值最小。
+
+2. 如果数值小的素数的幂次小于数值大的素数的幂,那么如果把这两个素数交换位置(幂次不变),那么所得的 $n$ 因子数量不变,但是 $n$ 的值变小。
 
 另外还有两个问题,
 
 1. 对于给定的 n,要枚举到哪一个素数呢?
 
-最极端的情况大不了就是 $n=p_{1}*p_{2} \cdots p_{n}$ ,所以只要连续素数连乘到刚好小于等于 n 就可以的呢。再大了,连全都一次幂,都用不了,当然就是用不到的啦!
+最极端的情况大不了就是 $n=p_{1}*p_{2} \cdots p_{n}$ ,所以只要连续素数连乘到刚好小于等于 $n$ 就可以的呢。再大了,连全都一次幂,都用不了,当然就是用不到的啦!
 
 2. 我们要枚举到多少次幂呢?
 
-我们考虑一个极端情况,当我们最小的素数的某个幂次已经比所给的 n(的最大值)大的话,那么展开成其他的形式,最大幂次一定小于这个幂次。unsigned long long 的最大值是 2 的 64 次方,所以我这边习惯展开成 2 的 64 次方。
+我们考虑一个极端情况,当我们最小的素数的某个幂次已经比所给的 $n$(的最大值)大的话,那么展开成其他的形式,最大幂次一定小于这个幂次。unsigned long long 的最大值是 2 的 64 次方,所以我这边习惯展开成 2 的 64 次方。
 
 细节有了,那么我们具体如何具体实现呢?
 
@@ -98,11 +101,11 @@ bool millerRabbin(int n) {
 
 1. 当前走到的数字已经大于我们想要的数字了
 
-2. 当前枚举的因子已经用不到了(和 1 重复了嘻嘻嘻)
+2. 当前枚举的因子已经用不到了(和 $1$ 重复了嘻嘻嘻)
 
 3. 当前因子大于我们想要的因子了
 
-4. 当前因子正好是我们想要的因子(此时判断是否需要更新最小 ans
+4. 当前因子正好是我们想要的因子(此时判断是否需要更新最小 $ans$
 
 然后 dfs 里面不断一层一层枚举次数继续往下迭代就好啦~~
 
@@ -121,10 +124,10 @@ bool millerRabbin(int n) {
 #define ULL unsigned long long
 #define INF ~0ULL
 int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
+
 ULL ans;
 int n;
+
 //depth: 当前在枚举第几个素数。num: 当前因子数。
 //temp: 当前因子数量为 num 的时候的数值。up:上一个素数的幂,这次应该小于等于这个幂次嘛
 void dfs(int depth,int temp,int num,int up){
@@ -138,8 +141,8 @@ void dfs(int depth,int temp,int num,int up){
         dfs(depth+1,temp = temp*p[depth],num*(i+1),i);
     }
 }
+
+
 int main(){
     while(scanf("%d",&n) != EOF){
         ans = INF;
@@ -160,11 +163,11 @@ http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562
 #include<cstdio>
 #include<iostream>
 #define ULL unsigned long long
+
 int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
 ULL n;
 ULL ans,ans_num;//ans 为 n 以内的最大反素数(会持续更新),ans_sum 为 ans 的因子数。
+
 void dfs(int depth,ULL temp,ULL num,int up){
     if(depth >= 16||temp > n)return;
     if(num > ans_num){
@@ -179,8 +182,8 @@ void dfs(int depth,ULL temp,ULL num,int up){
     }
     return;
 }
+
+
 int main(){
     while(scanf("%lld",&n) != EOF){
         ans_num = 0;
@@ -189,4 +192,4 @@ int main(){
     }
     return 0;
 }
-```
\ No newline at end of file
+```
index 3622c65..e705988 100644 (file)
@@ -1,6 +1,6 @@
 快速幂,是一种求 $a^b \bmod p$ 的方法,得益于将指数按二进制拆开的思想。\r
 \r
-事实上,根据模运算的性质,$a * b \bmod p = ((a \bmod p) * b) \bmod p$。那么我们也可以把2 $a^b \mod p$ 分解成一系列比较小的数的乘积。\r
+事实上,根据模运算的性质,$a \times b \bmod p = ((a \bmod p) \times b) \bmod p$。那么我们也可以把2 $a^b \mod p$ 分解成一系列比较小的数的乘积。\r
 \r
 如果把 $b$ 写作二进制为 $a_ta_{t-1} \cdots a_1a_0$,那么有:\r
 \r
@@ -14,7 +14,7 @@ $$
 $$\r
 \begin{aligned}\r
 a^b \bmod p & = (a^{a_t 2^t + \cdots + a_0 2^0}) \bmod p \\\\\r
-& = (..(a^{a_0 2^0} \bmod p) * \cdots * a^{a_52^5}) \bmod p\r
+& = (..(a^{a_0 2^0} \bmod p) \times \cdots \times a^{a_52^5}) \bmod p\r
 \end{aligned}\r
 $$\r
 \r
index 1ac2d55..f2a5d58 100644 (file)
@@ -1,10 +1,10 @@
 ## 素数筛法
 
-如果我们想要知道小于等于 n 有多少个素数呢?
+如果我们想要知道小于等于 $n$ 有多少个素数呢?
 
-一个自然的想法是我们对于小于等于 n 的每个数进行一次判定。这种暴力的做法显然不能达到最优复杂度,考虑如何优化呢。
+一个自然的想法是我们对于小于等于 $n$ 的每个数进行一次判定。这种暴力的做法显然不能达到最优复杂度,考虑如何优化呢。
 
-考虑这样一件事情:如果 x 是合数,那么 x 的倍数也一定是合数。利用这个结论,我们可以避免很多次不必要的检测。
+考虑这样一件事情:如果 $x$ 是合数,那么 $x$ 的倍数也一定是合数。利用这个结论,我们可以避免很多次不必要的检测。
 
 如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。
 
@@ -54,13 +54,13 @@ void init() {
 }
 ```
 
-上面代码中的 phi 数组,会在下面提到。
+上面代码中的 $phi$ 数组,会在下面提到。
 
 这种线性筛也称为欧拉筛法
 
 ## 筛法求欧拉函数
 
-注意到在线性筛中,每一个合数都是被最小的质因子筛掉。比如设 $p_1$ 是 $n$ 的最小质因子,$n' = \frac{n}{p_1}$,那么线性筛的过程中 $n$ 通过 $n' * p_1$ 筛掉。
+注意到在线性筛中,每一个合数都是被最小的质因子筛掉。比如设 $p_1$ 是 $n$ 的最小质因子,$n' = \frac{n}{p_1}$,那么线性筛的过程中 $n$ 通过 $n' \times p_1$ 筛掉。
 
 观察线性筛的过程,我们还需要处理两个部分,下面对$n' \bmod p_1$ 分情况讨论。
 
@@ -68,9 +68,9 @@ void init() {
 
 $$
 \begin{aligned}
-\varphi(n) & = n * \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
-& = p_1 * n' * \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
-& = p_1 * \varphi(n')
+\varphi(n) & = n \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
+& = p_1 \times n' \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
+& = p_1 \times \varphi(n')
 \end{aligned}
 $$
 
@@ -78,8 +78,8 @@ $$
 
 $$
 \begin{aligned}
-\varphi(n) & = \varphi(p_1) * \varphi(n') \\\\
-& = (p_1 - 1) * \varphi(n')
+\varphi(n) & = \varphi(p_1) \times \varphi(n') \\\\
+& = (p_1 - 1) \times \varphi(n')
 \end{aligned}
 $$
 
@@ -88,4 +88,3 @@ $$
 ## 筛法求约数个数
 
 ## 其他线性函数
-
index a5a3f92..cd222e5 100644 (file)
@@ -6,18 +6,18 @@
 
 题解:在近几年算法竞赛中,递推算法越来越重要:
 
-$$S_6^3=3 * S_5^3 + S_5^2$$
+$$S_6^3=3 \times S_5^3 + S_5^2$$
 
-$$S_5^3=3 * S_4^3 + S_4^2$$
+$$S_5^3=3 \times S_4^3 + S_4^2$$
 
-$$S_5^2=2 * S_4^2 + S_4^1$$
+$$S_5^2=2 \times S_4^2 + S_4^1$$
 
 第二类stirling数,显然拥有这样的性质:
 
-$$S_n^m = m * S_{n-1}^{m} + S_{n-1}^{m-1}$$
+$$S_n^m = m \times S_{n-1}^{m} + S_{n-1}^{m-1}$$
 
 $$S_n^1 = 1,S_n^0 = 0,S_n^n = 1$$
 
 而这些性质就可以总结成:
 
-$$S_n^3 = \frac{1}{2} * (3^{n-1}+1) - 2^{n-1}$$
\ No newline at end of file
+$$S_n^3 = \frac{1}{2} \times (3^{n-1}+1) - 2^{n-1}$$
index adf0b18..012a179 100644 (file)
@@ -8,7 +8,7 @@
 \r
 ### 形式\r
 \r
-对于序列上的区间询问问题,如果从$[l,r]$的答案能够$O(1)$扩展到$[l-1,r],[l+1,r],[l,r+1],[l,r-1]$(即与$[l,r]$相邻的区间)的答案,那么可以在$O(n\sqrt{n})$的复杂度内求出所有询问的答案。\r
+对于序列上的区间询问问题,如果从 $[l,r]$ 的答案能够 $O(1)$ 扩展到 $[l-1,r],[l+1,r],[l,r+1],[l,r-1]$(即与 $[l,r]$ 相邻的区间)的答案,那么可以在 $O(n\sqrt{n})$ 的复杂度内求出所有询问的答案。\r
 \r
 ### 实现\r
 \r
@@ -16,7 +16,7 @@
 \r
 ### 排序方法\r
 \r
-对于区间$[l,r]$,以l所在块的编号为第一关键字,r为第二关键字从小到大排序。\r
+对于区间$[l,r]$,以 $l$ 所在块的编号为第一关键字,$r$ 为第二关键字从小到大排序。\r
 \r
 ### 模板\r
 \r
@@ -46,31 +46,31 @@ void solve() {
 \r
 接着就到了莫队算法的精髓了,下面我们用通俗易懂的初中方法来证明它的时间复杂度是$O(n\sqrt{n})$;\r
 \r
-证:令每一块中L的最大值为 $max1,max2,max3,...,maxceil(\sqrt{n})$。\r
+证:令每一块中 $L$ 的最大值为 $max1,max2,max3,...,\operatorname{maxceil}(\sqrt{n})$。\r
 \r
-由第一次排序可知,$max1<=max2<=...<=maxceil(\sqrt{n})$。\r
+由第一次排序可知,$max1 \le max2 \le ... \le \operatorname{maxceil}(\sqrt{n})$。\r
 \r
 显然,对于每一块暴力求出第一个询问的时间复杂度为 $O(n)$。\r
 \r
-考虑最坏的情况,在每一块中,R 的最大值均为 n,每次修改操作均要将 L 由 $max_{i - 1}$ 修改至 $max_i$ 或由 $max_i$ 修改至 $max_{i - 1}$。\r
+考虑最坏的情况,在每一块中,$R$ 的最大值均为 $n$,每次修改操作均要将 $L$ 由 $max_{i - 1}$ 修改至 $max_i$ 或由 $max_i$ 修改至 $max_{i - 1}$。\r
 \r
-考虑 R:因为 R 在块中已经排好序,所以在同一块修改完它的时间复杂度为 $O(n)$。对于所有块就是 $O(n\sqrt{n})$。\r
+考虑 $R$:因为 $R$ 在块中已经排好序,所以在同一块修改完它的时间复杂度为 $O(n)$。对于所有块就是 $O(n\sqrt{n})$。\r
 \r
-重点分析 L:因为每一次改变的时间复杂度都是 $O(maxi-maxi-1)$ 的,所以在同一块中时间复杂度为 $O(\sqrt{n}*(max_i-max_{i-1}))$。\r
+重点分析 $L$:因为每一次改变的时间复杂度都是 $O(maxi-maxi-1)$ 的,所以在同一块中时间复杂度为 $O(\sqrt{n}*(max_i-max_{i-1}))$。\r
 \r
-将每一块L的时间复杂度合在一起,可以得到:\r
+将每一块 $L$ 的时间复杂度合在一起,可以得到:\r
 \r
 $$\r
 \begin{aligned}\r
-对于 L 的总时间复杂度为 & = O(\sqrt{n}(max1-1)+\sqrt{n}(max2-max1)+\sqrt{n}(max3-max2)+...+\sqrt{n}(maxceil(\sqrt{n})-maxceil(\sqrt{n}-1))) \\\\\r
-& = O(\sqrt{n}*(max1-1+max2-max1+max3-max2+...+maxceil(\sqrt{n}-1)-maxceil(\sqrt{n}-2)+maxceil(\sqrt{n})-maxceil(\sqrt{n}-1))) \\\\\r
-& = O(\sqrt{n}*(maxceil(\sqrt{n})-1))\r
+对于 L 的总时间复杂度为 & = O(\sqrt{n}(max1-1)+\sqrt{n}(max2-max1)+\sqrt{n}(max3-max2)+...+\sqrt{n}(\operatorname{maxceil}(\sqrt{n})-\operatorname{maxceil}(\sqrt{n}-1))) \\\\\r
+& = O(\sqrt{n}*(max1-1+max2-max1+max3-max2+...+\operatorname{maxceil}(\sqrt{n}-1)-\operatorname{maxceil}(\sqrt{n}-2)+\operatorname{maxceil}(\sqrt{n})-\operatorname{maxceil}(\sqrt{n}-1))) \\\\\r
+& = O(\sqrt{n}*(\operatorname{maxceil}(\sqrt{n})-1))\r
 \end{aligned}\r
 $$\r
 \r
 (裂项求和)\r
 \r
-由题可知 $maxceil(\sqrt{n})$ 最大为 n,所以 L 的总时间复杂度最坏情况下为 $O(n\sqrt{n})$。\r
+由题可知 $\operatorname{maxceil}(\sqrt{n})$ 最大为 $n$,所以 $L$ 的总时间复杂度最坏情况下为 $O(n\sqrt{n})$。\r
 \r
 综上所述,莫队算法的时间复杂度为 $O(n\sqrt{n})$;\r
 \r
@@ -79,33 +79,34 @@ $$
 [小Z的袜子](https://www.lydsy.com/JudgeOnline/problem.php?id=2038)\r
 \r
 思路:莫队算法模板题。\r
-对于区间$[l,r]$,以l所在块的编号为第一关键字,r为第二关键字从小到大排序。\r
-然后从序列的第一个询问开始计算答案,第一个询问通过直接暴力算出,复杂度为$O(n)$,后面的询问在前一个询问的基础上得到答案。\r
+对于区间 $[l,r]$,以 $l$ 所在块的编号为第一关键字,$r$ 为第二关键字从小到大排序。\r
+然后从序列的第一个询问开始计算答案,第一个询问通过直接暴力算出,复杂度为 $O(n)$,后面的询问在前一个询问的基础上得到答案。\r
 \r
 具体做法:\r
-对于区间$[i,i]$,由于区间只有一个元素,我们很容易就能知道答案。\r
+对于区间 $[i,i]$ ,由于区间只有一个元素,我们很容易就能知道答案。\r
 然后一步一步从当前区间(已知答案)向下一个区间靠近。\r
 \r
-我们设$col[i]$表示当前颜色i出现了多少次,$ans$当前共有多少种可行的配对方案(有多少种可以选到一双颜色相同的袜子),表示然后每次移动的时候更新答案——设当前颜色为k,如果是增长区间就是$ans$加上$C(col[k]+1,2)-C(col[k],2)$,如果是缩短就是$ans$减去$C(col[k],2)-C(col[k]-1,2)$。这应该很好理解。\r
-而这个询问的答案就是$ans/C(r-l+1,2)$\r
+我们设 $col[i]$ 表示当前颜色i出现了多少次,$ans$当前共有多少种可行的配对方案(有多少种可以选到一双颜色相同的袜子),表示然后每次移动的时候更新答案——设当前颜色为 $k$,如果是增长区间就是 $ans$ 加上 $C(col[k]+1,2)-C(col[k],2)$,如果是缩短就是 $ans$ 减去 $C(col[k],2)-C(col[k]-1,2)$。这应该很好理解。\r
+而这个询问的答案就是 $\frac{ans}{C(r-l+1,2)}$\r
 \r
 这里有个优化:\r
-$C(a,2)$=$a\times (a-1)/2$\r
-所以$C(a+1,2)-C(a,2)$=$(a+1)\times a/2-a\times (a-1)/2$=$a/2\times (a+1-a+1)$=$a/2\times 2$=$a$\r
-所以$C(col[k]+1,2)-C(col[k],2)$=$col[k]$\r
+$C(a,2)=a\times (a-1)/2$\r
+所以 $C(a+1,2)-C(a,2)=(a+1)\times a/2-a\times (a-1)/2=a/2\times (a+1-a+1)=a/2\times 2=a$\r
+所以$C(col[k]+1,2)-C(col[k],2)=col[k]$\r
 \r
 这样我们少算了很多东西呢!\r
-至少我的代码在BZOJ上测快了一倍。\r
+至少我的代码在 BZOJ 上测快了一倍。\r
 \r
-还有,算$C(a,2)$可以用位运算,$a/2$可以写成$a>>1$\r
+还有,算 $C(a,2)$ 可以用位运算,`a/2`可以写成`a>>1`\r
 \r
 算法总复杂度:$O(n\sqrt{n} )$\r
 \r
-下面的代码中mot表示答案的分母(mother),sub表示分子,sqn表示块的大小:$\sqrt{n}$(sqrt(n)),arr是输入的数组,node是存储询问的结构体,tab是询问序列(排序后的),col同上所述。\r
-<strong>注意:下面代码中的移动区间的4个for循环的位置很关键,不能改变它们之间的位置关系,不然会WA(因为有那个++l和--r)。</strong>\r
+下面的代码中 `mot` 表示答案的分母(mother),`sub` 表示分子,`sqn` 表示块的大小:$\sqrt{n}$,`arr` 是输入的数组,`node `是存储询问的结构体,`tab` 是询问序列(排序后的),`col` 同上所述。\r
+<strong>注意:下面代码中的移动区间的 4 个 for 循环的位置很关键,不能改变它们之间的位置关系,不然会 WA(因为有那个 ++l 和 --r)。</strong>\r
 代码:\r
 \r
-<pre><code class="cpp">#include <bits/stdc++.h>\r
+```cpp\r
+#include <bits/stdc++.h>\r
 #define bi(a) ((a-1)/sqn+1)\r
 using namespace std;\r
 typedef long long LL;\r
@@ -139,7 +140,7 @@ int main()
         gcdn=gcd(sub[i],mot[i]),printf("%lld/%lld\n",sub[i]/gcdn,mot[i]/gcdn);\r
     return 0;\r
 }\r
-</code></pre>\r
+```\r
 \r
 \r
 ## 带修改\r
@@ -148,31 +149,38 @@ int main()
 如果您还不会,请先阅读上面的“普通莫队算法”\r
 \r
 ### 特点\r
+\r
 - 用于离线处理区间问题\r
+\r
 - 仅含单点修改\r
-- 能$O(1)$转移区间(和普通莫队一样)\r
-- 分块的每一块的大小是$n^\frac{2}{3}$\r
-- 复杂度$O(n^\frac{5}{3})$\r
+\r
+- 能 $O(1)$ 转移区间(和普通莫队一样)\r
+\r
+- 分块的每一块的大小是 $n^\frac{2}{3}$\r
+\r
+- 复杂度 $O(n^\frac{5}{3})$\r
 \r
 带修改的莫队的询问排序方法为:\r
 \r
 - 第一关键字:左端点所在块编号,从小到大排序。\r
+\r
 - 第二关键字:右端点所在块编号,从小到大排序。\r
+\r
 - 第三关键字:**经历的修改次数**。也可以说是询问的先后,先询问的排前面。\r
 \r
 对于前后两个区间的转移:\r
 \r
-设当前询问为$a$,下一个询问为$b$,我们已知$a$,要求$b$。\r
+设当前询问为 $a$,下一个询问为 $b$,我们已知 $a$,要求 $b$。\r
 \r
 首先我们像普通莫队一样转移左右端点。\r
 \r
-这时候我们可能会发现**$a$和$b$的经历的修改次数不同**!\r
+这时候我们可能会发现**$a$ 和 $b$ 的经历的修改次数不同**!\r
 \r
 怎么办呢?\r
 \r
 然而,莫队就是个优雅的暴力。\r
 \r
-假如$a$较$b$少修改了$p$次,那我们就把这$p$次修改一个一个 **从前往后** 暴力地加上去。假如$a$较$b$多修改了$q$次,那我们就把这$q$次修改**从后往前**还原掉。\r
+假如 $a$ 较 $b$ 少修改了 $p$ 次,那我们就把这 $p$ 次修改一个一个 **从前往后** 暴力地加上去。假如 $a$ 较 $b$ 多修改了 $q$ 次,那我们就把这 $q$ 次修改**从后往前**还原掉。\r
 \r
 具体怎么做呢?我们来看一道例题。\r
 \r
@@ -182,7 +190,7 @@ int main()
 题目大意:给你一个序列,M个操作,有两种操作:\r
 \r
 1. 修改序列上某一位的数字\r
-2. 询问区间$[l,r]$中数字的种类数(多个相同的数字只算一个)\r
+2. 询问区间 $[l,r]$ 中数字的种类数(多个相同的数字只算一个)\r
 \r
 我们不难发现,如果不带操作1(修改)的话,我们就能轻松用普通莫队解决。\r
 \r
@@ -190,22 +198,23 @@ int main()
 \r
 先考虑普通莫队的做法:\r
 \r
-- 每次扩大区间时,每加入一个数字,则统计它已经出现的次数,如果加入前这种数字出现次数为0,则说明这是一种新的数字,答案+1。然后这种数字的出现次数+1\r
-- 每次减小区间时,每删除一个数字,则统计它删除后的出现次数,如果删除后这种数字出现次数为0,则说明这种数字已经从当前的区间内删光了,也就是当前区间减少了一种颜色,答案-1。然后这种数字的出现次数-1\r
+- 每次扩大区间时,每加入一个数字,则统计它已经出现的次数,如果加入前这种数字出现次数为 $0$,则说明这是一种新的数字,答案 $+1$。然后这种数字的出现次数 $+1$\r
+- 每次减小区间时,每删除一个数字,则统计它删除后的出现次数,如果删除后这种数字出现次数为 $0$,则说明这种数字已经从当前的区间内删光了,也就是当前区间减少了一种颜色,答案 $-1$。然后这种数字的出现次数 $-1$\r
 \r
 现在再来考虑修改:\r
 \r
-- 单点修改,把某一位的数字修改掉。假如我们是从一个经历修改次数为$i$的询问转移到一个经历修改次数为$j$的询问上,且$i<j$的话,我们就需要把第$i+1$个到第$j$个修改强行加上。\r
-- 假如$j<i$的话,则需要把第$i$个到第$j+1$个修改强行还原。\r
+- 单点修改,把某一位的数字修改掉。假如我们是从一个经历修改次数为 $i$ 的询问转移到一个经历修改次数为 $j$ 的询问上,且 $i<j$ 的话,我们就需要把第 $i+1$ 个到第 $j$ 个修改强行加上。\r
+\r
+- 假如 $j<i$ 的话,则需要把第 $i$ 个到第 $j+1$ 个修改强行还原。\r
 \r
-怎么强行加上一个修改呢?假设一个修改是修改第$pos$个位置上的颜色,原本$pos$上的颜色为$a$,修改后颜色为$b$,还假设当前莫队的区间扩展到了$[l,r]$。\r
+怎么强行加上一个修改呢?假设一个修改是修改第 $pos$ 个位置上的颜色,原本 $pos$ 上的颜色为 $a$,修改后颜色为 $b$,还假设当前莫队的区间扩展到了 $[l,r]$。\r
 \r
-- 加上这个修改:我们首先判断$pos$是否在区间$[l,r]$内。如果是的话,我们等于是从区间中删掉颜色$a$,加上颜色$b$,并且当前颜色序列的第$pos$项的颜色改成$b$。如果不在区间$[l,r]$内的话,我们就直接修改当前颜色序列的第$pos$项为$b$。\r
-- 还原这个修改:等于加上一个修改第$pos$项、把颜色$b$改成颜色$a$的修改。\r
+- 加上这个修改:我们首先判断 $pos$ 是否在区间 $[l,r]$ 内。如果是的话,我们等于是从区间中删掉颜色 $a$,加上颜色 $b$,并且当前颜色序列的第 $pos$ 项的颜色改成 $b$。如果不在区间 $[l,r]$ 内的话,我们就直接修改当前颜色序列的第 $pos$ 项为 $b$。\r
+- 还原这个修改:等于加上一个修改第$pos$项、把颜色 $b$ 改成颜色 $a$的修改。\r
 \r
 因此这道题就这样用带修改莫队轻松解决啦!\r
 \r
-记得前面说的一些普通莫队与带修改莫队不同的地方就行了,比如分块的每一块的大小是$n^\frac{2}{3}$。这个很重要。。。\r
+记得前面说的一些普通莫队与带修改莫队不同的地方就行了,比如分块的每一块的大小是 $n^\frac{2}{3}$。这个很重要。。。\r
 \r
 代码:\r
 ```cpp\r
index 0da96db..d1024c8 100644 (file)
@@ -8,17 +8,17 @@ A* 算法是 [BFS](/search/bfs) 的一种改进
 
 定义每个点的估价函数 $f(x)=g(x)+h(x)$
 
-A* 算法每次从队列中取出一个 f 最小的,然后更新相邻的状态
+A* 算法每次从队列中取出一个 $f$ 最小的,然后更新相邻的状态
 
 如果 $h\leq h*$,则 A* 算法能找到最优解
 
-上述条件下,如果 h 满足三角形不等式,则 A* 算法不会将重复结点扔进队列
+上述条件下,如果 $h$ 满足三角形不等式,则 A* 算法不会将重复结点扔进队列
 
 其实…… $h=0$ 时就是 [DFS](/search/dfs) 算法,$h=0$ 并且边权为 $1$ 时就是 [BFS](/search/BFS)
 
 一个很好的例子是八数码
 
-h 可以定义为,不在应该在的位置的数字个数
+$h$ 可以定义为,不在应该在的位置的数字个数
 
-容易发现 h 满足以上两个性质
+容易发现 $h$ 满足以上两个性质
 
index 65c645e..f1c9aba 100644 (file)
@@ -6,20 +6,26 @@
 
 KMP 自动机:一个不断读入待匹配串,每次匹配时走到接受状态的 DFA
 
-共有 m 个状态,第 i 个状态表示已经匹配了前 i 个字符
+共有 $m$ 个状态,第 $i$ 个状态表示已经匹配了前 $i$ 个字符
 
-`trans[i][x] = b[i] == x ? i + 1 : trans[next[i]][x]`
+$$
+trans[i][x] =
+\begin{cases}
+i + 1,  & \text{if $b[i] = x$} \\[2ex]
+trans[next[i]][x], & \text{else}
+\end{cases}
+$$
 
-(约定 next[0]=0
+(约定 $next[0]=0$
 
-我们发现trans[i] 只依赖于之前的值,所以可以跟 [KMP](/string/kmp) 一起求出来
+我们发现 $trans[i]$ 只依赖于之前的值,所以可以跟 [KMP](/string/kmp) 一起求出来
 
 时间和空间复杂度:$O(m|∑|)$
 
-一些细节:走到接受状态之后立即转移到该状态的 next
+一些细节:走到接受状态之后立即转移到该状态的 $next$
 
 ## AC算法就是Trie上的自动机
-注意在 [BFS](/search/bfs) 的同时求出 trans 即可
+注意在 [BFS](/search/bfs) 的同时求出 $trans$ 即可
 
 可以解决多串匹配问题
 
@@ -28,4 +34,3 @@ KMP 自动机:一个不断读入待匹配串,每次匹配时走到接受状
 前者能找到每次匹配,后者只能求出匹配次数(通过合并接受状态)
 
 ## AC 自动机的实现
-
index eea5162..e358911 100644 (file)
@@ -8,9 +8,9 @@ Hash 的核心思想在于,暴力算法中,单次比较的时间太长了,
 
 是比较第一个?最后一个?随便选一个?求所有字符的和?
 
-我们定义一个把 string 映射成 int 的函数 f,这个 f 称为是 Hash 函数。
+我们定义一个把 string 映射成 int 的函数 $f$,这个 $f$ 称为是 Hash 函数。
 
-我们希望这个函数 f 可以方便地帮我们判断两个字符串是否相等。
+我们希望这个函数 $f$ 可以方便地帮我们判断两个字符串是否相等。
 
 具体来说,我们希望在 Hash 函数值不一样的时候,两个字符串一定不一样。
 
@@ -20,11 +20,11 @@ Hash 的核心思想在于,暴力算法中,单次比较的时间太长了,
 
 时间复杂度和 Hash 的准确率。
 
-通常我们采用的是多项式 Hash 的方法,即 `f(s) = sum(s[i] * b^i) % M`
+通常我们采用的是多项式 Hash 的方法,即 $\operatorname{f}(s) = \sum s[i] \times b^i \pmod M$
 
-这里面的 b 和 M 需要选取得足够合适才行,以使得 Hash 的冲突尽量均匀。
+这里面的 $b$ 和 $M$ 需要选取得足够合适才行,以使得 Hash 的冲突尽量均匀。
 
-如果 b 和 M 互质,在输入随机的情况下,这个 Hash 函数在 $[0,M)$ 上每个值概率相等
+如果 $b$ 和 $M$ 互质,在输入随机的情况下,这个 Hash 函数在 $[0,M)$ 上每个值概率相等
 
 此时错误率为 $\frac1M$ (单次比较)
 
@@ -72,17 +72,17 @@ match(char *a, char *b, int n, int m) {
 
 先补充一些随机数学的知识(非严格地)
 
-现在考虑这 n 次比较,如果看成独立的,总错误率 $1-(1-1/M)^n$
+现在考虑这 $n$ 次比较,如果看成独立的,总错误率 $1-(1-1/M)^n$
 
-当 M >> n 时,总错误率接近于$\frac{n}{M}$
+当 $M >> n$ 时,总错误率接近于$\frac{n}{M}$
 
-当 M = n 时,接近于 $1-1/e (≈0.63)$
+当 $M = n$ 时,接近于 $1-1/e (≈0.63)$
 
 如果不是独立的,最坏情况也就是全部加起来,等于 $\frac{n}{M}$
 
-要改进错误率,可以增加 M
+要改进错误率,可以增加 $M$
 
-选取一个大的M,或者两个互质的小的 M
+选取一个大的M,或者两个互质的小的 $M$
 
 时间复杂度不变,单次错误率平方
 
index c7a4654..d4c45bd 100644 (file)
@@ -4,35 +4,35 @@
 
 主要思想是暴力的改进
 
-对于模式串 b,定义 next[]
+对于模式串 $b$,定义 $next[]$
 
-$$next[i] = max\{j\}\ s.t.\ 0 \leq j<i\ \&\&\ b[0..j-1]=b[i-j..i-1]$$
+$$next[i] = \max\{j\}\ s.t.\ 0 \leq j<i\ \&\&\ b[0..j-1]=b[i-j..i-1]$$
 
-【注意下标从 0 开始】【需要画图】
+【注意下标从 $0$ 开始】【需要画图】
 
-说好听点,a 的第 i 个前缀的最长的等于同长度的后缀的真前缀的长度
+说好听点,$a$ 的第 $i$ 个前缀的最长的等于同长度的后缀的真前缀的长度
 
-先不说 next 能干啥,想一想 next 怎么求
+先不说 $next$ 能干啥,想一想 $next$ 怎么求
 
-暴力肯定是不行的,每个 next 都要枚举长度(这东西不能二分),再依次比较……
+暴力肯定是不行的,每个 $next$ 都要枚举长度(这东西不能二分),再依次比较……
 
-我们换一个思路,从 next[1] 开始,一个一个求
+我们换一个思路,从 $next[1]$ 开始,一个一个求
 
-基础:显然 next[1]=0
+基础:显然 $next[1]=0$
 
-归纳:如果已知 next[0..i-1],那么 next[i]=next[i-1]+1 或 next[next[i-1]]+1 或…
+归纳:如果已知 $next[0..i-1]$,那么 $next[i]=next[i-1]+1$ 或 $next[next[i-1]]+1$ 或…
 
 这算法的复杂度是多少?
 
-考虑 next[i] 这个非负值,每次循环最多 +1,并且每次应用 next[] 的时候,至少-1,所以是 $O(m)$ 的
+考虑 $next[i]$ 这个非负值,每次循环最多 $+1$,并且每次应用 $next[]$ 的时候,至少 $-1$,所以是 $O(m)$ 的
 
-定义势能为next[i],每次循环势能++,每次取 next 势能减少得比实际代价多
+定义势能为 $next[i]$,每次循环势能 $+1$,每次取 $next$ 势能减少得比实际代价多
 
-均摊代价 $ \leq1$,势能取值范围0~2m,所以O(m)
+均摊代价 $\leq 1$,势能取值范围0~2m,所以 $O(m)$
 
-求出了 next,对字符串匹配有什么用呢?
+求出了 $next$,对字符串匹配有什么用呢?
 
-在a串上进行匹配,当失配时,移到next的位置
+在a串上进行匹配,当失配时,移到 $next$ 的位置
 
 时间复杂度是多少呢?(可以证明,是 $O(n+m)$ 的)
 
index 132de7f..0b6dd74 100644 (file)
@@ -49,3 +49,4 @@ match(char *a, char *b, int n, int m) {
 
 ## KMP 算法
 
+参见 [KMP](/string/kmp)
index 082a364..3462143 100644 (file)
 
 ## 在 Trie 上 KMP
 
-实际上要做的事情是求出 Trie 的每个节点的 next 值
+实际上要做的事情是求出 Trie 的每个节点的 $next$ 值
 
-当然,这里的 next 不再是一个值,而是相当于是一个指针 —— 它可能指向其他分支的节点。
+当然,这里的 $next$ 不再是一个值,而是相当于是一个指针 —— 它可能指向其他分支的节点。
 
-这时 next 的定义:最长的等于同长度的后缀的从根开始的路径的长度
+这时 $next$ 的定义:最长的等于同长度的后缀的从根开始的路径的长度
 
 求法跟 [KMP](/string/kmp) 中的一样,只是要改成在 Trie 上 [BFS](/search/bfs)
 
-复杂度:均摊分析失效了,其实只能在每条链上均摊分析,于是总复杂度为模式串长总和
\ No newline at end of file
+复杂度:均摊分析失效了,其实只能在每条链上均摊分析,于是总复杂度为模式串长总和