ABC408G sol: stern-brocot
arrow_king · · 题解
分数比大小题怎么能没有 Stern-Brocot 树的题解呢?
Stern-Brocot 树是一个维护所有最简分数的优雅结构。
假设我们有一个序列,初始含有两个分数
每一轮迭代,我们取序列中所有相邻的分数
比如,第二轮迭代后序列如下:
如果把插入新分数看做一个二叉树的结构,就可以抽象出 Stern-Brocot 树的结构:(图来自 oi-wiki)
这棵树有以下性质:(证明见 oi-wiki)
- 每次迭代后的分数序列都是单调递增的。
- 所有分数都是最简分数。
- 包含所有的正分数。
因为 Stern-Brocot 树是二叉搜索树,因此我们显然可以在上面进行二分搜索来查找到目标分数。由于向左走分数一定减小,向右走分数一定增大,因此我们只需要维护当前访问的区间是
查找
考虑到一次换方向会使得分子分母各至少乘
回到这个题,要求找到
单次查询的复杂度是
因为值域有点大,要写 __int128 才能通过。
pll ans;
i128 A,B,C,D;
il bool Less(i128 a,i128 b,i128 c,i128 d) {return a*d<b*c;}
il void solve(i128 a,i128 b,i128 c,i128 d) {
i128 n=a+c,m=b+d;
if(Less(A,B,n,m)&&Less(n,m,C,D)) return ans=mkp(n,m),void();
if(!Less(A,B,n,m)) {
i128 t=(m*A-n*B)/(c*B-d*A);
solve(n+t*c,m+t*d,c,d);
}
else {
i128 t=(m*C-n*D)/(a*D-b*C);
solve(a,b,n+t*a,m+t*b);
}
}