|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <cassert> |
| 4 | +#include <vector> |
| 5 | +#include <utility> |
| 6 | + |
| 7 | +// min((ax + b) mod m) for 0 <= x < n |
| 8 | +// 0 < n, 0 < m <= 1e9 |
| 9 | +// https://maspypy.com/library-checker-min-of-mod-of-linear |
| 10 | +template <class Int = int, class Long = long long> |
| 11 | +auto min_of_mod_of_linear(Int n, const Int &m, Int a, Int b) { |
| 12 | + |
| 13 | + // p = (xs, ys), v = (dx, dy) として |
| 14 | + // 列 (p, p + v, ..., p + (len - 1)v) が点群全体を包絡する |
| 15 | + struct Result { |
| 16 | + Int xs, ys; |
| 17 | + Int dx, dy; |
| 18 | + Int len; |
| 19 | + |
| 20 | + Int min() const { return ys + dy * (len - 1); } |
| 21 | + }; |
| 22 | + |
| 23 | + assert(0 < n); |
| 24 | + assert(0 < m); |
| 25 | + |
| 26 | + if (n > m) n = m; |
| 27 | + a = (m - a % m) % m; |
| 28 | + b = (b % m + m) % m; |
| 29 | + |
| 30 | + Int x = 0, y = b; |
| 31 | + |
| 32 | + Int p = 0, q = 1, r = 1, s = 0; // p/q <= a/m < r/s |
| 33 | + |
| 34 | + std::vector<Result> ret{Result{x, y, 0, 0, 1}}; |
| 35 | + |
| 36 | + while (true) { |
| 37 | + if (x + q > n - 1) break; |
| 38 | + |
| 39 | + const Long aqmp = (Long)a * q - (Long)m * p; |
| 40 | + if (aqmp == 0) break; // p/q == a/m |
| 41 | + |
| 42 | + const Long mras = (Long)m * r - (Long)a * s; |
| 43 | + |
| 44 | + assert(aqmp > 0); |
| 45 | + assert(mras > 0); |
| 46 | + |
| 47 | + if (const Long num = p + r, den = q + s; num * m <= den * a) { // num/den <= a/m 下側を更新 |
| 48 | + |
| 49 | + // q + is <= n - 1 - x |
| 50 | + |
| 51 | + // (p + ir) / (q + is) <= a/m (下側である条件) i >= 0 |
| 52 | + // <=> m(p + ir) <= a(q + is) <=> (mr - as)i <= aq - mp |
| 53 | + |
| 54 | + // a(q + is) - m(p + ir) <= Y (改善する条件) i >= 0 |
| 55 | + // (mr - as)i >= aq - mp - Y |
| 56 | + |
| 57 | + const Long iub = aqmp / mras; |
| 58 | + |
| 59 | + const Long imp_i_lb = std::max<Long>((std::max<Long>(aqmp - y, 0) + mras - 1) / mras, 0); |
| 60 | + |
| 61 | + if (imp_i_lb <= iub) { |
| 62 | + p += imp_i_lb * r; |
| 63 | + q += imp_i_lb * s; |
| 64 | + |
| 65 | + const Long c = (Long)a * q - (Long)m * p; |
| 66 | + assert(0 <= c); |
| 67 | + assert(c <= y); |
| 68 | + |
| 69 | + const Long kub = y / c; |
| 70 | + const Long k = std::min<Long>(kub, (n - 1 - x) / q); |
| 71 | + |
| 72 | + if (k) ret.push_back(Result{x + q, (Int)(y - c), q, (Int)-c, (Int)k}); |
| 73 | + |
| 74 | + x += q * k; |
| 75 | + y -= c * k; |
| 76 | + if (k < kub) break; |
| 77 | + continue; |
| 78 | + } else { |
| 79 | + p += iub * r; |
| 80 | + q += iub * s; |
| 81 | + } |
| 82 | + } else { // num/den > a/m 上側を更新 |
| 83 | + // (r + ip)/(s + iq) > a/m <=> m(r + ip) > a(s + iq) <=> (aq - mp)i < mr - as |
| 84 | + // -> i step 進んでもまだ超えない |
| 85 | + |
| 86 | + const Long i = (mras - 1) / aqmp; |
| 87 | + assert(i > 0); |
| 88 | + |
| 89 | + // x + (s + iq) <= n - 1 |
| 90 | + // const Long iub = (n - 1 - q - x - s) / q; |
| 91 | + // if (i > iub) break; |
| 92 | + |
| 93 | + r += (i - 1) * p; // NOTE: overflow |
| 94 | + s += (i - 1) * q; |
| 95 | + |
| 96 | + // まだ a/m < (p + r) / (q + s) |
| 97 | + assert((Long)a * (q + s) < (Long)m * (p + r)); |
| 98 | + |
| 99 | + r += p; |
| 100 | + s += q; |
| 101 | + |
| 102 | + // 逆転 |
| 103 | + assert((Long)a * (q + s) >= (Long)m * (p + r)); |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + // return y; |
| 108 | + return ret; |
| 109 | +} |
0 commit comments