Hướng dẫn giải của [Beginner Free Contest 13] XORSUM


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

Tìm K lớn nhất sao cho tổng (A_i xor K) không vượt quá M. Đề không có subtask, và K có thể lên tới khoảng 10^15, nên không có vét cạn hợp lý; đây là lời giải duy nhất, chỉ có một sub.

Sub 1: Tham lam theo bit — ~O(60 \cdot N)~

Gọi cntb là số Ai có bit b bằng 1. Mỗi bit b của K đóng góp độc lập: nếu đặt bit b của K là 1 thì tổng tăng (N - cntb) \cdot 2^b, nếu là 0 thì tổng tăng cntb \cdot 2^b. Bit từ 50 trở lên luôn làm tổng vượt M nên chỉ xét các bit 0 đến 49. Đi từ bit cao xuống thấp, đặt bit b bằng 1 nếu tổng hiện tại cộng chi phí bit b cộng chi phí nhỏ nhất của các bit thấp hơn vẫn không vượt M. Nếu chi phí nhỏ nhất của mọi bit vẫn vượt M thì in -1. Đúng 40/40 test.

Chương trình hoàn chỉnh

Hàm main đọc N, M và các Ai, đếm cntb, rồi gọi sub 1.

#include <bits/stdc++.h>
using namespace std;

// Chỉ có 1 sub: K có thể lên tới khoảng 10^15, không có vét cạn hợp lý, nên viết thẳng lời giải đầy đủ.

int n;
long long M;
long long A[1005];
const int BITS = 50;   // A_i, M <= 10^15 < 2^50; bit >= 50 của K làm B >= N * 2^50 > M nên không dùng

long long cnt[BITS];   // cnt[b] = số A_i có bit b bằng 1

// Sub 1 (duy nhất): O(60 * N)
// Các bit của K độc lập nhau: nếu K có bit b = 1 thì tổng tăng (N - cnt[b]) * 2^b,
// nếu bit b = 0 thì tổng tăng cnt[b] * 2^b. Chọn bit cao nhất có thể đặt 1 (theo tham lam từ bit cao xuống).
void sub1() {
    // chi phí nhỏ nhất của các bit thấp hơn b (khi chọn bit tối ưu cho từng bit)
    vector<long long> minRest(BITS + 1, 0);
    for (int b = 0; b < BITS; b++)
        minRest[b + 1] = minRest[b] + min(cnt[b], (long long)n - cnt[b]) * (1LL << b);
    // minRest[b] = chi phí nhỏ nhất của các bit 0..b-1

    if (minRest[BITS] > M) {   // ngay cả K tốt nhất cũng vượt M
        cout << -1 << "\n";
        return;
    }

    long long K = 0;
    long long cur = 0;   // tổng B hiện tại theo các bit đã chọn từ cao xuống
    for (int b = BITS - 1; b >= 0; b--) {
        long long cost1 = (long long)(n - cnt[b]) * (1LL << b);   // nếu bit b của K = 1
        long long cost0 = cnt[b] * (1LL << b);                    // nếu bit b của K = 0
        // thử đặt bit b = 1: tổng hiện tại + chi phí bit b + chi phí nhỏ nhất của các bit thấp
        if (cur + cost1 + minRest[b] <= M) {
            K |= (1LL << b);
            cur += cost1;
        } else {
            cur += cost0;
        }
    }
    cout << K << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> M;
    for (int i = 0; i < n; i++) {
        cin >> A[i];
        for (int b = 0; b < BITS; b++)
            if ((A[i] >> b) & 1) cnt[b]++;
    }
    sub1();
    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.