Hướng dẫn giải của [Free Contest 06] TIMER
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Hướng dẫn giải
Yêu cầu: đồng hồ tăng 1 mỗi giây, có tối đa ~k~ lần hack đưa giá trị lên bội nhỏ nhất của ~y~ không nhỏ hơn giá trị hiện tại. Tìm giá trị lớn nhất sau ~t~ giây.
Sub 1: Đệ quy thử mọi lựa chọn — ~O(2^t)~, dùng khi ~t \le 10~
Tại mỗi thời điểm, thử dừng hoặc hack (nếu còn lượt và giá trị chưa là bội của ~y~), hoặc đợi 1 giây. Hack không tốn thời gian nên được thử cả ở thời điểm cuối.
Nếu chỉ nộp sub này: đúng 2/18 test.
Sub 2: Công thức — ~O(1)~
Giá trị cuối bằng ~x + t~ cộng tổng phần tăng do hack. Lần hack đầu lúc ~\tau~ giây tăng ~r(\tau) = (-(x+\tau)) \bmod y~. Sau đó mỗi giây đợi rồi hack tăng đúng ~y-1~. Nếu ~k-1 \le t~ thì chọn ~\tau~ tốt nhất trong đoạn ~[0, t-(k-1)]~, ngược lại hack ngay từ đầu.
Nếu chỉ nộp sub này: đúng 18/18 test.
Chương trình hoàn chỉnh
Với ~t \le 10~ gọi sub 1, còn lại gọi sub 2. Sub sau có sai hoặc chưa kịp làm thì vẫn ăn điểm sub trước.
#include <bits/stdc++.h>
using namespace std;
long long X, Y, K, TT; // đồng hồ đang x, lỗ hổng chia hết cho y, k lần hack, t giây
// Sub 1: thử mọi lựa chọn (đợi 1 giây hoặc hack) đệ quy, dùng khi t <= 10
long long brute(long long v, long long tl, long long kl) {
long long best = v; // dừng ở đây (hack vẫn có thể làm ở thời điểm cuối)
if (kl > 0 && v % Y != 0) { // hack lên bội của y nhỏ nhất >= v, không tốn thời gian
long long nv = (v + Y - 1) / Y * Y;
best = max(best, brute(nv, tl, kl - 1));
}
if (tl > 0) best = max(best, brute(v + 1, tl - 1, kl)); // đợi 1 giây
return best;
}
void sub1() { cout << brute(X, TT, K) << "\n"; }
// Sub 2: công thức O(1) — lời giải đầy đủ
// Giá trị cuối = x + t + (tổng phần tăng do các lần hack). Lần hack đầu thực hiện lúc τ giây,
// tăng r(τ) = (-(x+τ)) mod y. Sau đó mỗi giây đợi rồi hack tăng đúng y-1.
// Nếu k-1 <= t: chọn τ tốt nhất trong [0, t-(k-1)] (còn đủ chỗ cho k-1 lần hack sau).
// Nếu k-1 > t: hack ngay lúc đầu (τ = 0) là tối ưu.
void sub2() {
long long r0 = ((-X) % Y + Y) % Y; // phần tăng nếu hack ngay lúc đầu
long long ans;
if (K - 1 <= TT) {
long long M = TT - (K - 1); // τ lớn nhất còn giữ đủ k-1 lần hack sau đó
// phần tăng tốt nhất với τ trong [0, M]: y-1 nếu tới được τ = r0+1, ngược lại r0
long long bg = (M >= r0 + 1 && r0 + 1 < Y) ? Y - 1 : r0;
ans = X + TT + bg + (K - 1) * (Y - 1);
} else {
ans = X + TT + r0 + TT * (Y - 1);
}
cout << ans << "\n";
}
int main() {
freopen("timer.in", "r", stdin);
freopen("timer.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> q;
while (q--) {
cin >> X >> Y >> K >> TT;
if (TT <= 10) sub1();
else sub2();
}
return 0;
}
Bình luận