Hướng dẫn giải của [Free Contest 06] TIMER


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
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

Hãy đọc nội quy trước khi bình luận.


Không có bình luận tại thời điểm này.