连分数有意义,当且仅当对应的极限有意义。其中,xk=[a0,a1,⋯,ak] 称为 x 的第 k 个 渐近分数(convergent)或 收敛子,而 rk=[ak,ak+1,⋯] 称为 x 的第 k 个 余项 或 完全商(complete quotient)。相应地,项 ak 有时也称为第 k 个 部分商(partial quotient)。
// Find the convergents of a continued fraction A.// Numerators and denominators stored separately in P and Q.auto convergents(std::vector<int> a) { std::vector<int> p = {0, 1}; std::vector<int> q = {1, 0}; for (auto it : a) { p.push_back(p.back() * it + p.end()[-2]); q.push_back(q.back() * it + q.end()[-2]); } return std::make_pair(p, q);}
Python
# Find the convergents of a continued fraction A.# Numerators and denominators stored separately in P and Q.def convergents(a): p = [0, 1] q = [1, 0] for it in a: p.append(p[-1] * it + p[-2]) q.append(q[-1] * it + q[-2]) return p, q
设 BA=[a0,a1,⋯,ak]。上面证明了 pkqk−1−pk−1qk=(−1)k−1。将 pk 和 qk 替换为 A 和 B,得到
Aqk−1−Bpk−1=(−1)k−1g,
其中,g=gcd(A,B)。如果 g 整除 C,则一组解为 x=(−1)k−1gCqk−1 和 y=(−1)kgCpk−1;否则无解。
[list2tab]
C++
// Return (x,y) such that Ax+By=C.// Assume that such (x,y) exists.auto dio(int A, int B, int C) { std::vector<int> p, q; std::tie(p, q) = convergents(fraction(A, B)); C /= A / p.back(); int t = p.size() % 2 ? -1 : 1; return std::make_pair(t * C * q.end()[-2], -t * C * p.end()[-2]);}
Python
# Return (x, y) such that Ax+By=C.# Assume that such (x, y) exists.def dio(A, B, C): p, q = convergents(fraction(A, B)) C //= A // p[-1] # divide by gcd(A, B) t = (-1) if len(p) % 2 else 1 return t * C * q[-2], -t * C * p[-2]
就称有理数 qp 是实数 x 的 第一类最佳逼近(best approximation of the first kind)。
第一类最佳逼近未必是渐近分数,而是一类更宽泛的分数。
中间分数
设实数 x 有渐近分数 xk+1=[a0,a1,⋯,ak,ak+1],且整数 t 满足 0≤t≤ak+14,则分数 xk,t=[a0,a1,⋯,ak,t] 称为 x 的 中间分数(intermediate fraction)、半收敛子(semiconvergent)或 次渐近分数(secondary convergent)。5
// Expand [..., n] to [..., n-1, 1] if needed.void expand(std::vector<int>& a) { if (a.size() == 1 || a.back() > 1) { --a.back(); a.push_back(1); }}// Check if a is smaller than b.bool less_than(std::vector<int> a, std::vector<int> b) { expand(a); expand(b); for (int i = 0; i < a.size() - 1 || i < b.size() - 1; ++i) { int d = (i < a.size() - 1 ? a[i] : 0) - (i < b.size() - 1 ? b[i] : 0); if (i & 1) d = -d; if (d < 0) { return true; } else if (d > 0) { return false; } } return false;}
Python
# Expand [..., n] to [..., n-1, 1] if needed.def expand(a): if a[-1] != 1 or len(a) == 1: a[-1] -= 1 a.append(1) return a# Check if a is smaller than b.def less_than(a, b): a = expand(a) b = expand(b) a = [(-1) ** i * a[i] for i in range(len(a))] b = [(-1) ** i * b[i] for i in range(len(b))] return a < b
// Get X +- EPSILON.auto pm_eps(std::vector<int> a) { constexpr int inf = 0x3f3f3f3f; // Deal with empty continued fraction for 1/0. if (a.empty()) { a.emplace_back(inf); } auto b = a; expand(b); a.emplace_back(inf); b.emplace_back(inf); return less_than(a, b) ? std::make_pair(a, b) : std::make_pair(b, a);}// Find the lexicographically smallest (q, p)// such that p0/q0 < p/q < p1/q1.auto middle(int p0, int q0, int p1, int q1) { auto a0 = pm_eps(fraction(p0, q0)).second; auto a1 = pm_eps(fraction(p1, q1)).first; std::vector<int> a; for (int i = 0; i < a0.size() || i < a1.size(); ++i) { if (a0[i] == a1[i]) { a.emplace_back(a0[i]); } else { a.emplace_back(std::min(a0[i], a1[i]) + 1); break; } } auto pq = convergents(a); return std::make_pair(pq.first.back(), pq.second.back());}
Python
# Get X +- EPSILON.def pm_eps(a): # Deal with empty continued fraction for 1/0. if not a: a.append(float("inf")) b = expand(a.copy()) a.append(float("inf")) b.append(float("inf")) return (a, b) if less_than(a, b) else (b, a)# Find the lexicographically smallest (q, p)# such that p0/q0 < p/q < p1/q1.def middle(p0, q0, p1, q1): a0 = pm_eps(fraction(p0, q0))[1] a1 = pm_eps(fraction(p1, q1))[0] a = [] for i in range(min(len(a0), len(a1))): if a0[i] == a1[i]: a.append(a0[i]) else: a.append(int(min(a0[i], a1[i])) + 1) break p, q = convergents(a) return p[-1], q[-1]
void solve() { int n; std::cin >> n; std::vector<int> C(n), J(n); // p0/q0 < y/x < p1/q1 int p0 = 0, q0 = 1, p1 = 1, q1 = 0; bool fail = false; for (int i = 0; i < n; ++i) { std::cin >> C[i] >> J[i]; if (i) { int A = C[i] - C[i - 1]; int B = J[i] - J[i - 1]; if (A <= 0 && B <= 0) { fail = true; break; } else if (B > 0 && A < 0) { // y/x > (-A)/B if B > 0 if ((-A) * q0 > p0 * B) { p0 = -A; q0 = B; } } else if (B < 0 && A > 0) { // y/x < A/(-B) if B < 0 if (A * q1 < p1 * (-B)) { p1 = A; q1 = -B; } } } } if (fail || p0 * q1 >= p1 * q0) { printf("IMPOSSIBLE\n"); } else { auto pq = middle(p0, q0, p1, q1); printf("%d %d\n", pq.first, pq.second); }}
Python
def solve(): n = int(input()) C = [0] * n J = [0] * n # p0/q0 < y/x < p1/q1 p0, q0 = 0, 1 p1, q1 = 1, 0 fail = False for i in range(n): C[i], J[i] = map(int, input().split()) if i > 0: A = C[i] - C[i - 1] B = J[i] - J[i - 1] if A <= 0 and B <= 0: fail = True break elif B > 0 and A < 0: # y/x > (-A)/B if B > 0 if (-A) * q0 > p0 * B: p0, q0 = -A, B elif B < 0 and A > 0: # y/x < A/(-B) if B < 0 if A * q1 < p1 * (-B): p1, q1 = A, -B if fail or p0 * q1 >= p1 * q0: return "IMPOSSIBLE" p, q = middle(p0, q0, p1, q1) return str(p) + " " + str(q)
想要了解更多 Stern–Brocot 树的性质和应用,可以参考其主条目页面。
分式线性变换
和连分数相关的另一个重要概念是所谓的分式线性变换。
分式线性变换
分式线性变换(fractional linear transformation)是指函数 L:R→R,使得
#include <algorithm>#include <iostream>#include <tuple>#include <vector>constexpr int M = 1e9 + 7;// FLTs. Essentially 2x2 matrix.struct FracLinearTrans { int mat[4]; FracLinearTrans() : mat{} {} FracLinearTrans(int x) : mat{x, 1, 1, 0} {} FracLinearTrans(int a, int b, int c, int d) : mat{a, b, c, d} {} FracLinearTrans operator*(const FracLinearTrans& rhs) const { return FracLinearTrans( ((long long)mat[0] * rhs.mat[0] + (long long)mat[1] * rhs.mat[2]) % M, ((long long)mat[0] * rhs.mat[1] + (long long)mat[1] * rhs.mat[3]) % M, ((long long)mat[2] * rhs.mat[0] + (long long)mat[3] * rhs.mat[2]) % M, ((long long)mat[2] * rhs.mat[1] + (long long)mat[3] * rhs.mat[3]) % M); } FracLinearTrans inv() const { return FracLinearTrans(mat[3], M - mat[1], M - mat[2], mat[0]); }};int main() { int n, q; std::cin >> n >> q; // Get prefix sum of FLTs. std::vector<FracLinearTrans> ps(1, {1, 0, 0, 1}); ps.reserve(n + 1); for (int i = 1; i <= n; ++i) { int a; std::cin >> a; ps[i] = ps[i - 1] * FracLinearTrans(a); } // Query. for (; q; --q) { int l, r; std::cin >> l >> r; // Difference. auto res = ps[l - 1].inv() * ps[r]; int u = res.mat[0], d = res.mat[2]; // Correct signs. if (!(l & 1)) { if (u) u = M - u; if (d) d = M - d; } printf("%d %d\n", u, d); } return 0;}
Python
# PYTHON IS TOO SLOW TO PASS THIS PROBLEM.# JUST FOR REFERENCE.M = 10**9 + 7def mul(a, b): return ( (a[0] * b[0] + a[1] * b[2]) % M, (a[0] * b[1] + a[1] * b[3]) % M, (a[2] * b[0] + a[3] * b[2]) % M, (a[2] * b[1] + a[3] * b[3]) % M, )def inv(a): return (a[3], M - a[1], M - a[2], a[0])n, q = map(int, input().split())ps = [(1, 0, 0, 1)]# Get presum.for a in map(int, input().split()): ps.append(mul(ps[-1], (a, 1, 1, 0)))for _ in range(q): l, r = map(int, input().split()) res = mul(inv(ps[l - 1]), ps[r]) u, d = res[0], res[2] if l % 2 == 0: if u: u = M - u if d: d = M - d print(u, d)
设连分数 x=[a0,a1,a2,⋯],且存在自然数 K 和正整数 L 使得对于任何 k≥K,都有 ak=ak+L,就称连分数 x 为 循环连分数(periodic continued fraction)。满足这个条件的最小的 L 称为它的最小正周期,而在连分数中重复出现的 ak,⋯,ak+L−1 序列就称为它的循环节。利用循环节,循环连分数可以写作 x=[a0,⋯,ak−1,ak,⋯,ak+L−1]。如果 K 可以取作 0,即 x=[a0,⋯,aL−1],就称它为 纯循环连分数(purely periodic continued fraction),否则称它为 混循环连分数(eventually periodic continued fraction)。
// Return the continued fraction and minimal positive period// of a quadratic irrational (x + y * sqrt(n)) / z.auto quadratic_irrational(int x, int y, int z, int n) { int p = x * z; int d = n * y * y * z * z; int q = z * z; int dd = (int)sqrt(n) * y * z; int i = 0; std::vector<int> a; std::unordered_map<size_t, int> used; while (!used.count(((1LL << 32) * p) | q)) { a.emplace_back((p + dd) / q); used[((1LL << 32) * p) | q] = i++; p = a.back() * q - p; q = (d - p * p) / q; } return std::make_pair(a, i - used[((1LL << 32) * p) | q]);}
Python
# Return the continued fraction and minimal positive period# of a quadratic irrational (x + y * sqrt(n)) / z.def quadratic_irrational(x, y, z, n): p = x * z d = n * y * y * z * z q = z * z dd = floor(sqrt(n)) * y * z i = 0 a = [] used = dict() while (p, q) not in used: a.append((p + dd) // q) used[p, q] = i p = a[-1] * q - p q = (d - p * p) // q i += 1 return a, i - used[p, q]
#include <algorithm>#include <cmath>#include <iostream>#include <tuple>#include <unordered_map>#include <vector>// Return the continued fraction and minimal positive period// of a quadratic irrational (x + y * sqrt(n)) / z.auto quadratic_irrational(int x, int y, int z, int n) { int p = x * z; int d = n * y * y * z * z; int q = z * z; int dd = (int)sqrt(n) * y * z; int i = 0; std::vector<int> a; std::unordered_map<size_t, int> used; while (!used.count(((1LL << 32) * p) | q)) { a.emplace_back((p + dd) / q); used[((1LL << 32) * p) | q] = i++; p = a.back() * q - p; q = (d - p * p) / q; } return std::make_pair(a, i - used[((1LL << 32) * p) | q]);}// Fractional Linear Transformation.struct FracLinearTrans { static constexpr int M = 1e9 + 7; int mat[4]; FracLinearTrans(int a, int b, int c, int d) : mat{a, b, c, d} {} FracLinearTrans operator*(const FracLinearTrans& rhs) const { return FracLinearTrans( ((long long)mat[0] * rhs.mat[0] + (long long)mat[1] * rhs.mat[2]) % M, ((long long)mat[0] * rhs.mat[1] + (long long)mat[1] * rhs.mat[3]) % M, ((long long)mat[2] * rhs.mat[0] + (long long)mat[3] * rhs.mat[2]) % M, ((long long)mat[2] * rhs.mat[1] + (long long)mat[3] * rhs.mat[3]) % M); }};int main() { int x, k, L; std::cin >> x >> k; std::vector<int> a; std::tie(a, L) = quadratic_irrational(0, 1, 1, x); // L==a.size()-1 for sqrt(x) FracLinearTrans cyc(1, 0, 0, 1); for (int i = a.size() - 1; i; --i) { cyc = FracLinearTrans(a[i], 1, 1, 0) * cyc; } // 1/0=Inf. FracLinearTrans res(0, 1, 0, 0); // Tail terms. for (int i = k % L; i; --i) { res = FracLinearTrans(a[i], 1, 1, 0) * res; } // Binary exponentiation. for (int b = k / L; b; b >>= 1) { if (b & 1) res = cyc * res; cyc = cyc * cyc; } // First term. res = FracLinearTrans(a[0], 1, 1, 0) * res; printf("%d/%d", res.mat[1], res.mat[3]); return 0;}
Python
from math import sqrt, floor# Return the continued fraction and minimal positive period# of a quadratic irrational (x + y * sqrt(n)) / z.def quadratic_irrational(x, y, z, n): p = x * z d = n * y * y * z * z q = z * z dd = floor(sqrt(n)) * y * z i = 0 a = [] used = dict() while (p, q) not in used: a.append((p + dd) // q) used[p, q] = i p = a[-1] * q - p q = (d - p * p) // q i += 1 return a, i - used[p, q]# Compose (A[0]*x + A[1]) / (A[2]*x + A[3]) and (B[0]*x + B[1]) / (B[2]*x + B[3])def combine(A, B): return [ t % mod for t in [ A[0] * B[0] + A[1] * B[2], A[0] * B[1] + A[1] * B[3], A[2] * B[0] + A[3] * B[2], A[2] * B[1] + A[3] * B[3], ] ]# Binary exponentiation.def bpow(A, n): return ( [1, 0, 0, 1] if not n else combine(A, bpow(A, n - 1)) if n % 2 else bpow(combine(A, A), n // 2) )mod = 10**9 + 7x, k = map(int, input().split())a, T = quadratic_irrational(0, 1, 1, x)A = (1, 0, 0, 1) # (x + 0) / (0*x + 1) = x# apply ak + 1/x = (ak*x+1)/(1x+0) to (Ax + B) / (Cx + D)for i in reversed(range(1, len(a))): A = combine([a[i], 1, 1, 0], A)C = (0, 1, 0, 0) # = 1 / 0while k % T: i = k % T C = combine([a[i], 1, 1, 0], C) k -= 1C = combine(bpow(A, k // T), C)C = combine((a[0], 1, 1, 0), C)print(str(C[1]) + "/" + str(C[3]))
// Find [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute// upper convex hull of lattice points on 0 <= x <= N and 0 <= y <= r * x,// where r = [a0, a1, a2, ...] and there are ah[i]-1 integer points on the// segment between r[i] and r[i+1].auto hull(std::vector<int> a, int N) { std::vector<int> p, q; std::tie(p, q) = convergents(a); int t = N / q.back(); std::vector<int> ah = {t}; std::vector<int> ph = {0, t * p.back()}; std::vector<int> qh = {0, t * q.back()}; for (int i = q.size() - 1; i; --i) { if (i % 2) { while (qh.back() + q[i - 1] <= N) { t = (N - qh.back() - q[i - 1]) / q[i]; int dp = p[i - 1] + t * p[i]; int dq = q[i - 1] + t * q[i]; int k = (N - qh.back()) / dq; ah.push_back(k); ph.push_back(ph.back() + k * dp); qh.push_back(qh.back() + k * dq); } } } return make_tuple(ah, ph, qh);}
Python
# Find [ah, ph, qh] such that points r[i]=(ph[i], qh[i]) constitute# upper convex hull of lattice points on 0 <= x <= N and 0 <= y <= r * x,# where r = [a0, a1, a2, ...] and there are ah[i]-1 integer points on the# segment between r[i] and r[i+1].def hull(a, N): p, q = convergents(a) t = N // q[-1] ah = [t] ph = [0, t * p[-1]] qh = [0, t * q[-1]] for i in reversed(range(len(q))): if i % 2 == 1: while qh[-1] + q[i - 1] <= N: t = (N - qh[-1] - q[i - 1]) // q[i] dp = p[i - 1] + t * p[i] dq = q[i - 1] + t * q[i] k = (N - qh[-1]) // dq ah.append(k) ph.append(ph[-1] + k * dp) qh.append(qh[-1] + k * dq) return ah, ph, qh
// Find (x, y) such that y = (A*x+B)/C,// such that Cy - Ax is max and 0 <= x <= N.auto closest(int A, int B, int C, int N) { // y <= (A*x + B)/C <=> diff(x, y) <= B auto diff = [&](int x, int y) -> int { return C * y - A * x; }; std::vector<int> p, q; std::tie(p, q) = convergents(fraction(A, C)); int qh = 0, ph = B / C; for (int i = 2; i < q.size() - 1; ++i) { if (i % 2 == 0) { while (diff(qh + q[i + 1], ph + p[i + 1]) <= B) { int t = 1 + (diff(qh + q[i - 1], ph + p[i - 1]) - B - 1) / (-diff(q[i], p[i])); int dp = p[i - 1] + t * p[i]; int dq = q[i - 1] + t * q[i]; int k = (N - qh) / dq; if (k == 0) { return std::make_pair(qh, ph); } if (diff(dq, dp) != 0) { k = std::min(k, (B - diff(qh, ph)) / diff(dq, dp)); } qh += k * dq; ph += k * dp; } } } return std::make_pair(qh, ph);}auto solve(int A, int B, int N) { int x, y; std::tie(x, y) = closest(A, N % A, B, N / A); return std::make_pair(N / A - x, y);}
Python
# Find (x, y) such that y = (A*x+B) // C,# such that Cy - Ax is max and 0 <= x <= N.def closest(A, B, C, N): # y <= (A*x + B)/C <=> diff(x, y) <= B def diff(x, y): return C * y - A * x p, q = convergents(fraction(A, C)) qh, ph = 0, B // C for i in range(2, len(q) - 1): if i % 2 == 0: while diff(qh + q[i + 1], ph + p[i + 1]) <= B: t = 1 + (diff(qh + q[i - 1], ph + p[i - 1]) - B - 1) // ( -diff(q[i], p[i]) ) dp = p[i - 1] + t * p[i] dq = q[i - 1] + t * q[i] k = (N - qh) // dq if k == 0: return qh, ph if diff(dq, dp) != 0: k = min(k, (B - diff(qh, ph)) // diff(dq, dp)) qh, ph = qh + k * dq, ph + k * dp return qh, phdef solve(A, B, N): x, y = closest(A, N % A, B, N // A) return N // A - x, y
// Find sum of floor(k * x) for k in [1, N] and x = [a0; a1, a2, ...]int sum_floor(std::vector<int> a, int N) { N++; std::vector<int> ah, ph, qh; std::tie(ah, ph, qh) = hull(a, N); // The number of lattice points within a vertical right trapezoid // on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has // a+1 integer points on the segment (0; y1) - (dx; y2). auto picks = [](int y1, int y2, int dx, int a) -> int { int b = y1 + y2 + a + dx; int A = (y1 + y2) * dx; return (A + b) / 2 - y2; // = (A - b + 2) // 2 + b - (y2 + 1) }; int ans = 0; for (size_t i = 1; i < qh.size(); i++) { ans += picks(ph[i - 1], ph[i], qh[i] - qh[i - 1], ah[i - 1]); } return ans - N;}
Python
# Find sum of floor(k * x) for k in [1, N] and x = [a0; a1, a2, ...].def sum_floor(a, N): N += 1 ah, ph, qh = hull(a, N) # The number of lattice points within a vertical right trapezoid # on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has # a+1 integer points on the segment (0; y1) - (dx; y2) but with # the number of points on the vertical right line excluded. def picks(y1, y2, dx, a): b = y1 + y2 + a + dx A = (y1 + y2) * dx return (A + b) // 2 - y2 # = (A - b + 2) // 2 + b - (y2 + 1) ans = 0 for i in range(1, len(qh)): ans += picks(ph[i - 1], ph[i], qh[i] - qh[i - 1], ah[i - 1]) return ans - N
// Find convex hull of lattice (x, y) such that C*y <= A*x+B.auto hull(int A, int B, int C, int N) { auto diff = [&](int x, int y) -> int { return C * y - A * x; }; auto a = fraction(A, C); std::vector<int> p, q; std::tie(p, q) = convergents(a); std::vector<int> ah(0), ph(1, B / C), qh(1, 0); auto insert = [&](int dq, int dp) -> void { int k = (N - qh.back()) / dq; if (diff(dq, dp) > 0) { k = std::min((int)k, (B - diff(qh.back(), ph.back())) / diff(dq, dp)); } ah.emplace_back(k); qh.emplace_back(qh.back() + k * dq); ph.emplace_back(ph.back() + k * dp); }; for (int i = 1; i < q.size() - 1; ++i) { if (i % 2 == 0) { while (diff(qh.back() + q[i + 1], ph.back() + p[i + 1]) <= B) { int t = (B - diff(qh.back() + q[i + 1], ph.back() + p[i + 1])) / (-diff(q[i], p[i])); int dp = p[i + 1] - t * p[i]; int dq = q[i + 1] - t * q[i]; if (dq < 0 || qh.back() + dq > N) break; insert(dq, dp); } } } insert(q.back(), p.back()); for (int i = q.size() - 1; i; --i) { if (i % 2 == 1) { while (qh.back() + q[i - 1] <= N) { int t = (N - qh.back() - q[i - 1]) / q[i]; int dp = p[i - 1] + t * p[i]; int dq = q[i - 1] + t * q[i]; insert(dq, dp); } } } return std::make_tuple(ah, ph, qh);}// Sum of floor (Ax+B)/M from 0 to N-1.auto solve(int N, int M, int A, int B) { std::vector<int> ah, ph, qh; std::tie(ah, ph, qh) = hull(A, B, M, N); // The number of lattice points within a vertical right trapezoid // on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has // a+1 integer points on the segment (0; y1) - (dx; y2) but with // the number of points on the vertical right line excluded. auto picks = [&](int y1, int y2, int dx, int a) -> int { int b = y1 + y2 + a + dx; int A = (y1 + y2) * dx; return (A + b) / 2 - y2; // = (A - b + 2) // 2 + b - (y2 + 1) }; int ans = 0; for (int i = 1; i < qh.size(); ++i) { ans += picks(ph[i - 1], ph[i], qh[i] - qh[i - 1], ah[i - 1]); } return ans - N;}
Python
# Find convex hull of lattice (x, y) such that C*y <= A*x+B.def hull(A, B, C, N): def diff(x, y): return C * y - A * x a = fraction(A, C) p, q = convergents(a) ah = [] ph = [B // C] qh = [0] def insert(dq, dp): k = (N - qh[-1]) // dq if diff(dq, dp) > 0: k = min(k, (B - diff(qh[-1], ph[-1])) // diff(dq, dp)) ah.append(k) qh.append(qh[-1] + k * dq) ph.append(ph[-1] + k * dp) for i in range(1, len(q) - 1): if i % 2 == 0: while diff(qh[-1] + q[i + 1], ph[-1] + p[i + 1]) <= B: t = (B - diff(qh[-1] + q[i + 1], ph[-1] + p[i + 1])) // ( -diff(q[i], p[i]) ) dp = p[i + 1] - t * p[i] dq = q[i + 1] - t * q[i] if dq < 0 or qh[-1] + dq > N: break insert(dq, dp) insert(q[-1], p[-1]) for i in reversed(range(len(q))): if i % 2 == 1: while qh[-1] + q[i - 1] <= N: t = (N - qh[-1] - q[i - 1]) // q[i] dp = p[i - 1] + t * p[i] dq = q[i - 1] + t * q[i] insert(dq, dp) return ah, ph, qhdef solve(N, M, A, B): ah, ph, qh = hull(A, B, M, N) # The number of lattice points within a vertical right trapezoid # on points (0; 0) - (0; y1) - (dx; y2) - (dx; 0) that has # a+1 integer points on the segment (0; y1) - (dx; y2) but with # the number of points on the vertical right line excluded. def picks(y1, y2, dx, a): b = y1 + y2 + a + dx A = (y1 + y2) * dx return (A + b) // 2 - y2 # = (A - b + 2) // 2 + b - (y2 + 1) ans = 0 for i in range(1, len(qh)): ans += picks(ph[i - 1], ph[i], qh[i] - qh[i - 1], ah[i - 1]) return ans - N
// Find Q that minimizes Q*r mod m for 1 <= k <= n < m.int mod_min(int r, int n, int m) { auto a = fraction(r, m); std::vector<int> p, q; std::tie(p, q) = convergents(a); for (int i = 2; i < q.size(); ++i) { if (i % 2 == 1 && (i + 1 == q.size() || q[i + 1] > n)) { int t = (n - q[i - 1]) / q[i]; return q[i - 1] + t * q[i]; } } return 0;}
Python
# Find Q that minimizes Q*r mod m for 1 <= k <= n < m.def mod_min(r, n, m): a = fraction(r, m) p, q = convergents(a) for i in range(2, len(q)): if i % 2 == 1 and (i + 1 == len(q) or q[i + 1] > n): t = (n - q[i - 1]) // q[i] return q[i - 1] + t * q[i] return 0