#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; //Integer-Related typedef long long i64; typedef unsigned long long ui64; //vector-Related template using vv = vector>; template using vvv = vector>>; typedef vector vi64; typedef vector vbool; typedef vv vvi64; typedef vv vvbool; typedef vvv vvvi64; typedef vvv vvvbool; typedef pair pi64; typedef vector vstr; //これで最大値優先 template> using prque = priority_queue, Pred>; //set-Related #include typedef set si64; typedef multiset msi64; typedef unordered_set usi64; //Calculation-Related inline i64 rm(const i64 l, const i64 r) { i64 val = l % r; if (val >= 0) { return val; } else { return val + r; } } template T r_accumulate(const vector& vec) { return std::accumulate(vec.begin(), vec.end(), T{}); } template void r_insert(vector& to, vector& from) { ranges::copy(from, back_inserter(to)); } constexpr std::string repeater3030(const std::string& s, int number) { std::string cur = ""; for (i64 i = 0; i < number; i++) { cur += s; } return cur; } constexpr std::string repeater3030(const char& c, int number) { string s(1, c); return repeater3030(s, number); } constexpr i64 pow_i64(i64 base, i64 exp) { i64 res = 1LL; while (exp > 0LL) { if (exp & 1) { res *= base; } exp >>= 1LL; base *= base; } return res; } template T eqmin(T& a, T b) { a = min(a, b); return a; } template T eqmax(T& a, T b) { a = max(a, b); return a; } //Output-Related template std::ostream& operator<<(std::ostream& os, const std::pair& p) { os << "(" << p.first << ", " << p.second << ")"; return os; } template inline void output(const T elem) { std::cout << elem << std::endl; } #ifndef __INTELLISENSE__ template inline void output(const pair elem) { std::cout << "(" << elem.first << "," << elem.second << ")" << std::endl; } template inline void output(const vector& vec) { for (T elem : vec) { std::cout << elem << " "; } std::cout << std::endl; } template inline void output(const vector>& vvec) { for (vector vec : vvec) { std::cout << "("; for (T elem : vec) { std::cout << elem << " "; } std::cout << ")"; } std::cout << std::endl; } template inline void output(const vector>>& vvvec) { for (vector> vvec : vvvec) { std::cout << "["; for (vector vec : vvec) { std::cout << "("; for (T elem : vec) { std::cout << elem << " "; } std::cout << ")"; } std::cout << "]" << std::endl; } } #endif template inline void output_iter(const T& iter) { for (auto&& elem : iter) { std::cout << elem << " "; } std::cout << std::endl; } /** * ACLibrary-fewnwicktree * Ref : https://atcoder.github.io/ac-library/master/document_ja/ */ /** * ACLibrary-internaltypetraits * Ref : https://atcoder.github.io/ac-library/master/document_ja/ */ #include namespace atcoder { namespace internal { #ifndef _MSC_VER template using is_signed_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using is_unsigned_int128 = typename std::conditional::value || std::is_same::value, std::true_type, std::false_type>::type; template using make_unsigned_int128 = typename std::conditional::value, __uint128_t, unsigned __int128>; template using is_integral = typename std::conditional::value || is_signed_int128::value || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using is_signed_int = typename std::conditional<(is_integral::value && std::is_signed::value) || is_signed_int128::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional<(is_integral::value && std::is_unsigned::value) || is_unsigned_int128::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional< is_signed_int128::value, make_unsigned_int128, typename std::conditional::value, std::make_unsigned, std::common_type>::type>::type; #else template using is_integral = typename std::is_integral; template using is_signed_int = typename std::conditional::value && std::is_signed::value, std::true_type, std::false_type>::type; template using is_unsigned_int = typename std::conditional::value && std::is_unsigned::value, std::true_type, std::false_type>::type; template using to_unsigned = typename std::conditional::value, std::make_unsigned, std::common_type>::type; #endif template using is_signed_int_t = std::enable_if_t::value>; template using is_unsigned_int_t = std::enable_if_t::value>; template using to_unsigned_t = typename to_unsigned::type; } // namespace internal } // namespace atcoder namespace atcoder { // Reference: https://en.wikipedia.org/wiki/Fenwick_tree template struct fenwick_tree { using U = internal::to_unsigned_t; public: fenwick_tree() : _n(0) {} explicit fenwick_tree(int n) : _n(n), data(n) {} void add(int p, T x) { assert(0 <= p && p < _n); p++; while (p <= _n) { data[p - 1] += U(x); p += p & -p; } } T sum(int l, int r) { assert(0 <= l && l <= r && r <= _n); return sum(r) - sum(l); } private: int _n; std::vector data; U sum(int r) { U s = 0; while (r > 0) { s += data[r - 1]; r -= r & -r; } return s; } }; } // namespace atcoder /** * ACLibrary-segtree * Ref : https://atcoder.github.io/ac-library/master/document_ja/ */ /** * ACLibrary-internalbit * Ref : https://atcoder.github.io/ac-library/master/document_ja/ */ #ifdef _MSC_VER #include #endif #if __cplusplus >= 202002L #endif namespace atcoder { namespace internal { #if __cplusplus >= 202002L using std::bit_ceil; #else // @return same with std::bit::bit_ceil unsigned int bit_ceil(unsigned int n) { unsigned int x = 1; while (x < (unsigned int)(n)) x *= 2; return x; } #endif // @param n `1 <= n` // @return same with std::bit::countr_zero int countr_zero(unsigned int n) { #ifdef _MSC_VER unsigned long index; _BitScanForward(&index, n); return index; #else return __builtin_ctz(n); #endif } // @param n `1 <= n` // @return same with std::bit::countr_zero constexpr int countr_zero_constexpr(unsigned int n) { int x = 0; while (!(n & (1 << x))) x++; return x; } } // namespace internal } // namespace atcoder namespace atcoder { #if __cplusplus >= 201703L template struct segtree { static_assert(std::is_convertible_v>, "op must work as S(S, S)"); static_assert(std::is_convertible_v>, "e must work as S()"); #else template struct segtree { #endif public: segtree() : segtree(0) {} explicit segtree(int n) : segtree(std::vector(n, e())) {} explicit segtree(const std::vector& v) : _n(int(v.size())) { size = (int)internal::bit_ceil((unsigned int)(_n)); log = internal::countr_zero((unsigned int)size); d = std::vector(2 * size, e()); for (int i = 0; i < _n; i++) d[size + i] = v[i]; for (int i = size - 1; i >= 1; i--) { update(i); } } void set(int p, S x) { assert(0 <= p && p < _n); p += size; d[p] = x; for (int i = 1; i <= log; i++) update(p >> i); } S get(int p) const { assert(0 <= p && p < _n); return d[p + size]; } S prod(int l, int r) const { assert(0 <= l && l <= r && r <= _n); S sml = e(), smr = e(); l += size; r += size; while (l < r) { if (l & 1) sml = op(sml, d[l++]); if (r & 1) smr = op(d[--r], smr); l >>= 1; r >>= 1; } return op(sml, smr); } S all_prod() const { return d[1]; } template int max_right(int l) const { return max_right(l, [](S x) { return f(x); }); } template int max_right(int l, F f) const { assert(0 <= l && l <= _n); assert(f(e())); if (l == _n) return _n; l += size; S sm = e(); do { while (l % 2 == 0) l >>= 1; if (!f(op(sm, d[l]))) { while (l < size) { l = (2 * l); if (f(op(sm, d[l]))) { sm = op(sm, d[l]); l++; } } return l - size; } sm = op(sm, d[l]); l++; } while ((l & -l) != l); return _n; } template int min_left(int r) const { return min_left(r, [](S x) { return f(x); }); } template int min_left(int r, F f) const { assert(0 <= r && r <= _n); assert(f(e())); if (r == 0) return 0; r += size; S sm = e(); do { r--; while (r > 1 && (r % 2)) r >>= 1; if (!f(op(d[r], sm))) { while (r < size) { r = (2 * r + 1); if (f(op(d[r], sm))) { sm = op(d[r], sm); r--; } } return r + 1 - size; } sm = op(d[r], sm); } while ((r & -r) != r); return 0; } private: int _n, size, log; std::vector d; void update(int k) { d[k] = op(d[2 * k], d[2 * k + 1]); } }; } // namespace atcoder using namespace atcoder; /** * Graph一式 * Author : WhiteKnight */ struct Dest { i64 to; i64 cost; }; typedef vector> wgraphList;//Weighted,List typedef vector> sgraphList;//Simple,List typedef vector vedge; typedef vvi64 wgraphMat;//Weighted,Matrix wgraphList translate_simple_weight(sgraphList graph) { i64 N = graph.size(); wgraphList res(N); for (i64 i = 0; i < N; i++) { for (auto&& e : graph[i]) { res[i].push_back({e,1}); } } return res; } //コストを無視し構造のみのグラフを返す sgraphList translate_weight_simple(wgraphList graph){ i64 N = graph.size(); sgraphList res(N); for (i64 i = 0; i < N; i++) { for (auto&& e : graph[i]) { res[i].push_back(e.to); } } return res; } template wgraphList translate_mat_list(wgraphMat graph){ i64 N = graph.size(); wgraphList res(N); for (i64 i = 0; i < N; i++) { for (i64 j = 0; j < N; j++) { if(graph[i][j] >= INF){ continue; } res[i].push_back({ j,graph[i][j] }); } } return res; } //Main Flow /** * Stern-Brocot-Tree * Author: WhiteKnight */ struct Rational{ i64 num; i64 dom; }; struct SternBrocotTree{ private: //max_dom:included,val:included //first:左,second:右 pair get_nearest(i64 max_dom, Rational val){ assert(val.dom > 0); assert(max_dom >= 1); Rational l = {0,1}; Rational r = {1,0}; Rational cur = {1,1}; pair res = { cur,cur }; while (true) { if(cur.num*val.dom < val.num*cur.dom){ //to right i64 dp_x = (cur.dom*val.num - cur.num*val.dom)/(r.num*val.dom - r.dom*val.num); if((cur.dom*val.num - cur.num*val.dom) != (r.num*val.dom - r.dom*val.num)){ dp_x++; } bool stopper = false; if(r.dom > 0){ stopper = (max_dom - cur.dom) / r.dom < dp_x; eqmin(dp_x, (max_dom - cur.dom) / r.dom); } assert(dp_x >= 0); if(dp_x == 0){ break; } cur.num += dp_x * r.num; cur.dom += dp_x * r.dom; l.num = cur.num - r.num; l.dom = cur.dom - r.dom; if(stopper){ res.first = cur; break; }else{ res.first = l; res.second = cur; } }else if(cur.num*val.dom == val.num*cur.dom){ res.first = cur; res.second = cur; return res; }else{ //to left i64 dp_x = (cur.num*val.dom - val.num*cur.dom)/(val.num*l.dom-l.num*val.dom); if((cur.num*val.dom - val.num*cur.dom) != (val.num*l.dom-l.num*val.dom)){ dp_x++; } bool stopper = (max_dom - cur.dom) / l.dom < dp_x; eqmin(dp_x, (max_dom - cur.dom) / l.dom); assert(dp_x >= 0); if(dp_x == 0){ break; } cur.num += dp_x * l.num; cur.dom += dp_x * l.dom; r.num = cur.num - l.num; r.dom = cur.dom - l.dom; if(stopper){ res.second = cur; break; }else{ res.second = r; res.first = cur; } } } return res; } public: //max_dom:included,val:included Rational left_nearest(i64 max_dom, Rational val){ auto res = get_nearest(max_dom,val); return res.first; } //max_dom:included,val:included Rational right_nearest(i64 max_dom, Rational val){ auto res = get_nearest(max_dom,val); return res.second; } }; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cout << setprecision(15); i64 P, Q; cin >> P >> Q; SternBrocotTree sbt; auto R = sbt.right_nearest(Q-1,{P,Q}); auto L = sbt.left_nearest(Q-1,{P,Q}); output(R.dom+R.num+L.dom+L.num); return 0; }