#include // assert #include // cin, cout, ios #include // swap, pair #include namespace mp = boost::multiprecision; using bigint = mp::cpp_int; // 商を無限大方向に切り下げる除算(剰余が0でない時の符号は除数の符号に一致) template constexpr std::pair floor_divmod(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 euclid_divmod(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 bigint * @param m bigint * @param a bigint * @param b bigint * @param c bigint * @param d bigint * @return bigint */ bigint mwf(bigint n, bigint m, bigint a, bigint b, bigint c, bigint d) { assert(n > 0 && m > 0); bigint sum_acc = 0, max_acc = b * euclid_divmod(d, m).first; while(true) { auto [q1, r1] = euclid_divmod(c, m); c = r1; a = a + b * q1; auto [q2, r2] = euclid_divmod(d, m); d = r2; sum_acc = sum_acc + b * q2; assert(0 <= c && c < m && 0 <= d && d < m); max_acc = mp::max(max_acc, sum_acc); auto y_max = (c * (n - 1) + d) / m; if(y_max == 0) { return mp::max(max_acc, sum_acc + a * (n - 1)); } if(a >= 0) { max_acc = mp::max(max_acc, sum_acc + a * (n - 1) + b * y_max); } else { sum_acc = sum_acc + a + b; } n = y_max; d = m - d - 1; std::swap(a, b); std::swap(c, m); } } /** * x_min(D, A, B, K) を半開区間二分探索 [0, A'BK+2) で求めます(解なしは -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 */ bigint compute_xmin(bigint D, bigint A, bigint B, bigint K) { assert(D > 0 && A > 0 && B > 0); if (K < 0) { return 0; } bigint gcd_da = mp::gcd(D, A); bigint d_red = D / gcd_da, a_red = A / gcd_da; bigint m_red = (a_red * B) / d_red, r_red = (a_red * B) % d_red; bigint 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 を設定 bigint 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) { bigint 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'; } }