Stern-Brocot 树
由两个初始值 \(0\over 1\) 和 \(1\over0\),由两个相邻的数 \(a\over b\) 和 \(c\over d\) 会生成数 \(a + c\over b + d\)。这由图片可以非常直观地看出。形态类似于一棵树。
每个点上有一个"三元组"\((a,b,c)\),\(\left(\dfrac{0}{1},\dfrac{1}{1},\dfrac{1}{0}\right)\) 作为根节点,且在每个节点 \(\left(\dfrac{a}{b},\dfrac{p}{q},\dfrac{c}{d}\right)\) 后
\(\left(\dfrac{a}{b},\dfrac{a+p}{b+q},\dfrac{p}{q}\right)\) 为其左儿子,\(\left(\dfrac{p}{q},\dfrac{p+c}{q+d},\dfrac{c}{d}\right)\) 为其右儿子
根据构造,Stern-Brocot 树显然是分数的 二叉搜索树,即树上节点的中序遍历得到的分数序列是递增的。除此之外,他还满足如下两个重要性质:
- 最简性:Stern-Brocot 树构造出来的所有分数都是最简分数
- 完全性:Stern-Brocot 树能构造出来任意一个正的最简分数
完全性是显然的,因为往子树搜索一定会让分子或者分母变大,这样对于任意一个最简分数 \(\frac{p}{q}\),搜索次数是有限的。
最简性要用线性代数和裴蜀定理。
SB树的作用
在 Stern-Brocot 树上查找一个确定的分数 \(\frac{p}{q}\)
每次暴力往左/右跳的复杂度是 \(O(p+q)\) 的,无法接受。
这个复杂度高的原因是我们经常会连续往左/往右跳很多步,而一次往左跳再接一次往右跳(或者一次往右跳接一次往左跳)一定会让分子分母都至少变为原来的两倍。所以“拐弯”的次数是 \(O(\log (p+q))\) 的。
只要我们能快速确定每次拐弯的位置就行了。
如果要查找的分数 \(\dfrac{p}{q}\) 落入 \(\dfrac{a}{b}\) 和 \(\dfrac{c}{d}\) 之间,那么连续 \(t\) 次向右移动时,右侧边界保持不动,而左侧节点移动到 \(\dfrac{a+tc}{b+td}\) 处;反过来,连续 \(t\) 次向左移动时,左侧边界保持不动,而右侧节点移动到 \(\dfrac{ta+c}{tb+d}\) 处。因此,可以直接通过 \(\dfrac{a+tc}{b+td}<\dfrac{p}{q}\) 或 \(\dfrac{p}{q}<\dfrac{ta+c}{tb+d}\) 解出向右和向左移动的次数。
复杂度 \(O(\log(p+q))\)。
- 大部分时候,我们在树上查找的分数其实是不确定的,比如我们需要根据当前分数的值跑一个二分查找的检验算法来决定具体是往左走还是往右走。
这个时候,把刚才通过解方程来确定次数的部分改为二分连续向左/向右的次数 \(t\) 即可。复杂度 \(O(\log^2(p+q))\)。