#include // assert #include // cin, cout, ios #include // swap, pair #include namespace mp = boost::multiprecision; using bigint = mp::cpp_int; template T gcd(T a, T b){while(b != 0){a %= b; std::swap(a, b);}return a;} template bool chmin(T& a, T b){if(a > b){a = b; return true;} return false;} template bool chmax(T& a, T b){if(a < b){a = b; return true;} return false;} template constexpr std::pair divmod(T a, T b){return {a / b, a % b};} // 商を無限大方向に切り下げる除算(剰余が0でない時の符号は除数の符号に一致) template constexpr std::pair divmod_floor(T a, T b) { // 標準の truncating 除算を使う T q = a / b; T r = a % b; // もし符号が食い違っていたら 1 調整 if ((r != 0) && ((r > 0) != (b > 0))) { q -= 1; r += b; } return {q, r}; } // 剰余が非負になる除算(ユークリッド除算) template constexpr std::pair divmod_euclid(T a, T b) { // 標準の truncating 除算を使う T q = a / b; T r = a % b; // 剰余が負なら 1 調整 if (r < 0) { if (b > 0) { q -= 1; r += b; } else { q += 1; r -= b; } } return {q, r}; } /** * Max Weighted Floor (mwf) の非再帰実装。 * mwf(n,m,a,b,c,d) = max_{0 <= x < n} a*x + b*floor((c*x + d)/m) * * 前提: * * n > 0, m > 0 * * 計算量/メモリ: * * 時間: O(log m)(ユークリッド互除法的再帰による構造縮約) * * 追加メモリ: O(1) * * @param n T * @param m T * @param a T * @param b T * @param c T * @param d T * @return T */ template T mwf(T n, T m, T a, T b, T c, T d) { assert(n > 0 && m > 0); auto [qc, rc] = divmod_euclid(c, m); c = rc; a += b * qc; auto [qd, rd] = divmod_euclid(d, m); d = rd; T sum_acc = b * qd; T max_acc = sum_acc; while(true) { assert(n > 0 && m > 0 && 0 <= c && c < m && 0 <= d && d < m); T n1 = n - 1; // 0 <= x < n における y = floor((c*x+d)/m) の最大値 y_max を計算 T y_max = (c * n1 + d) / m; // 現在の小問題における x = 0, n-1 のときの値を max_acc に反映 chmax(max_acc, sum_acc); chmax(max_acc, sum_acc + a * n1 + b * y_max); // x = 0, n-1 のいずれかで最大値を取るのが確定なら終了 if(y_max == 0 || (a >= 0 && b >= 0) || (a <= 0 && b <= 0)) { return max_acc; } // 小問題へのパラメータ変換 if(a < 0) { sum_acc += a + b; } n = y_max; // y_max > 0 d = m - d - 1; // 0 <= d < m std::swap(a, b); std::swap(c, m); // 0 < c,m // 正規化: c,d を m で割った余りに変換し、a,b,sum_acc を更新 auto [qc, rc] = divmod(c, m); c = rc; a += b * qc; auto [qd, rd] = divmod(d, m); d = rd; sum_acc += b * qd; } } /** * Δ(D,A,B,x) = floor(x/D) - floor( (floor(x/A) * floor(A*B/D)) / B ) において、 * u_min(D,A,B,K) = min { u >= 0 | Δ(D,A,B,u*D) > K } を半開区間二分探索 [0, A'BK+2) で求め、 * x_min(D,A,B,K) = min { x >= 0 | Δ(D,A,B,x) > K } = u_min(D,A,B,K)*D を返します(解なしは -1)。 * * 前提: * * D > 0, A > 0, B > 0, K >= 0(整数) * 手順概要: * 1) 既約化: g = gcd(D, A), D' = D/g, A' = A/g * 2) (M', R') = divmod(A' * B, D')(A'B = D'*M' + R') * 3) 閾値 T_Δ = B*K を設定 * 4) E(u) = B*u - M'*floor(D'u / A') * 5) F(N) = max_{0 <= u < N} E(u) を mwf で評価(N > 0, m = A' > 0) * 6) 区間 [0, A'BK+2) で述語 [F(u) <= T_Δ] を二分探索し、 * F(u) <= T_Δ となる最大の u 、つまり T_Δ < E(u) となる最小の u を特定。x = D*u を返す。 * * 備考: * * R' = 0 かつ D'K + 1 >= A' のときは解が存在しないため -1 を返します。 * * 解が存在する場合、 u_min は必ず [0, A'BK+2) の範囲に存在します。 * @param D bigint * @param A bigint * @param B bigint * @param K bigint * @return bigint */ template T compute_xmin(T D, T A, T B, T K) { assert(D > 0 && A > 0 && B > 0); if (K < 0) { return 0; } // 既約化 (D', A') = (D/gcd(D,A), A/gcd(D,A)) T gcd_da = gcd(D, A); T d_red = D / gcd_da, a_red = A / gcd_da; // (M', R') = divmod(A'B, D') // A'B = D'M' + R' auto [m_red, r_red] = divmod(a_red * B, d_red); T t_delta = B * K; // 解なしをパラメータを用いて判定 if (r_red == 0 && d_red * K + 1 >= a_red) { return -1; } // [0, hi) の半開区間、緩い上界 A'BK+1 を包括する hi = A'BK+2 を設定 T lo = 0, hi = a_red * B * K + 2; // F(hi) > T の不変条件を確認 assert(!(mwf(hi, a_red, B, -m_red, d_red, 0) <= t_delta)); // F(lo) <= T, F(hi) > T の不変条件で u_min を二分探索 while (lo + 1 < hi) { T mid = (lo + hi) / 2; if (mwf(mid, a_red, B, -m_red, d_red, 0) <= t_delta) { lo = mid; } else { hi = mid; } } // lo = u_min, hi = lo + 1. // x_min = D * u_min を返す return D * lo; } int main() { std::cin.tie(nullptr); std::ios::sync_with_stdio(false); int t; bigint d, a, b, k, ans; std::cin >> t; for(int i = 0; i < t; i++) { std::cin >> d >> a >> b >> k; assert(1 <= d && 1 <= a && 1 <= b && 0 <= k); ans = compute_xmin(d, a, b, k); std::cout << ans << '\n'; } }