Hướng dẫn giải của Xử lí BIT


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.

Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.

Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.

Bạn phải đăng nhập để mở lời giải này.

Đăng nhập

Tác giả: Hiếu Nguyễn, duclong0050, Miracal2929, sussyboy

Hiểu bài toán

Bài toán yêu cầu tính tổng phức tạp: $$\sum^{N}_{i=1}\sum^{N}_{j=1} (A_i \text{or} A_j) \text{xor} (A_j \text{and} A_i)$$ Tuy nhiên, theo các giải pháp và mẫu thử, công thức thực tế được sử dụng là: $$\sum^{N}_{i=1}\sum^{N}_{j=1} (A_i \text{or} A_j) \oplus (A_i \text{and} A_j)$$ hoặc một biến thể tương tự cho cặp $(i, j)$ với $i \le j$. Ta có nhận xét bitwise:

  • $Ai \text{or} Aj = (Ai \text{and} Aj) + (Ai \text{xor} Aj)$
  • Do đó: $(Ai \text{or} Aj) \oplus (Ai \text{and} Aj) = ((Ai \text{and} Aj) + (Ai \text{xor} Aj)) \oplus (Ai \text{and} Aj)$. Vì bitwise sum không đơn giản bằng XOR, ta xét từng bit.
  • Bit của $(Ai \text{or} Aj)$ là 1 nếu ít nhất một trong hai bit của $Ai, Aj$ là 1.
  • Bit của $(Ai \text{and} Aj)$ là 1 nếu cả hai bit đều là 1.
  • Bit của phép XOR này là 1 nếu bit của $(Ai \text{or} Aj)$ khác bit của $(Ai \text{and} Aj)$.
    • Trường hợp 1: Cả hai bit đều 0. OR=0, AND=0. XOR=0.
    • Trường hợp 2: Một bit 0, một bit 1. OR=1, AND=0. XOR=1.
    • Trường hợp 3: Cả hai bit đều 1. OR=1, AND=1. XOR=0.
  • Kết luận: Kết quả chỉ có bit tại vị trí $k$ là 1 nếu chính xác một trong hai số $Ai, Aj$ có bit $k$ là 1.
  • Với mỗi vị trí bit $k$, số lượng cặp $(i, j)$ thỏa mãn là $cnt1[k] \times cnt0[k]$ (chọn 1 số có bit 1 và 1 số có bit 0).
  • Tổng giá trị đóng góp của bit $k$ là $cnt1[k] \times cnt0[k] \times 2^k$.
  • Với tổng $i \le j$, ta có thể tính tổng toàn bộ $i, j$ rồi trừ đi các phần tử chéo (i=j) hoặc điều chỉnh. Tuy nhiên, các solution bên dưới tính tổng $i, j$ không giới hạn $i \le j$ (tức là $N^2$ cặp). Nếu $N$ lớn, tổng này sẽ rất lớn.

Giả sử bài toán yêu cầu tính tổng cho tất cả các cặp $(i, j)$ (hoặc $\frac{1}{2}$ tổng này), giải pháp tập trung vào việc đếm bit.

Các cách tiếp cận

Cách Brute Force (Sơ đồ gốc)
void solve_brute() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];
    long long total = 0;
    for (int i = 0; i < N; ++i) {
        for (int j = 0; j < N; ++j) {
            total += (A[i] | A[j]) ^ (A[i] & A[j]);
        }
    }
    cout << total << "\n";
}
  • Time Complexity: O(N^2)
  • Space Complexity: O(N)

Giải pháp này sử dụng hai vòng lặp lồng nhau để duyệt qua tất cả các cặp (i, j) có thể. Với mỗi cặp, nó tính trực tiếp biểu thức $(Ai \text{or} Aj) \oplus (Ai \text{and} Aj)$ và cộng dồn vào tổng. Đây là cách làm trực quan nhất nhưng không đủ nhanh cho N lên tới $10^5$.

Cách Bit Manipulation (Phân tích Bit)
#include <bits/stdc++.h>
using namespace std;

void solve() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];

    long long total = 0;
    // Duyệt qua 31 bit (vì a_i < 2^31)
    for (int k = 0; k < 31; ++k) {
        long long count_1 = 0;
        for (int x : A) {
            if (x & (1 << k)) count_1++;
        }
        long long count_0 = N - count_1;
        // Đóng góp của bit thứ k là số lượng cặp (i, j) mà bit này khác nhau
        total += count_1 * count_0 * (1LL << k);
    }
    cout << total << "\n";
}
  • Time Complexity: O(31 * N)
  • Space Complexity: O(N)

Thay vì duyệt cặp, ta duyệt theo từng bit (từ 0 đến 30). Với mỗi bit $k$, ta đếm số lượng số có bit $k$ là 1 ($cnt1$) và số lượng số có bit $k$ là 0 ($cnt0$). Theo phân tích toán học, bit $k$ của kết quả chỉ bằng 1 nếu một trong hai số có bit 1 và số còn lại có bit 0. Số lượng cặp thỏa mãn là $cnt1 \times cnt0$. Giá trị đóng góp là $cnt1 \times cnt0 \times 2^k$. Độ phức tạp là $O(31N)$, rất nhanh.

Cách Bit Manipulation (Tối ưu nhập xuất)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

void solve() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];

    ll total = 0;
    for (int k = 0; k < 31; ++k) {
        ll count_1 = 0;
        for (int i = 0; i < N; ++i) {
            if (A[i] & (1 << k)) count_1++;
        }
        ll count_0 = N - count_1;
        total += count_1 * count_0 * (1LL << k);
    }
    cout << total << "\n";
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--) solve();
    return 0;
}
  • Time Complexity: O(31 * N)
  • Space Complexity: O(N)

Đây là phiên bản hoàn chỉnh của phương pháp Bit Manipulation, bao gồm các tối ưu hóa nhập xuất chuẩn trong lập trình thi đấu (ios_base::sync_with_stdio(false); cin.tie(nullptr);) để đảm bảo tốc độ đọc dữ liệu nhanh nhất có thể. Logic tính toán tương tự như cách 2.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(N^2) O(N) Brute Force (Sơ đồ gốc)
2 O(31 * N) O(N) Bit Manipulation (Phân tích Bit)
3 O(31 * N) O(N) Bit Manipulation (Tối ưu nhập xuất)

Bài học kinh nghiệm

  • Biến đổi đại số: $(Ai \text{or} Aj) \oplus (Ai \text{and} Aj)$ chỉ bằng 1 tại những bit mà $Ai$ và $Aj$ khác nhau (một bên 1, một bên 0).
  • Tách rời các bit: Bài toán có thể chia thành 31 bài toán con độc lập, mỗi bài tính tổng đóng góp của một bit.
  • Tính toán theo cặp: Với mỗi bit, số lượng cặp $(i, j)$ thỏa mãn điều kiện bit khác nhau là $cnt{1} \times cnt{0}$.

Lỗi thường gặp

  • Kiểu dữ liệu: Kết quả có thể rất lớn (lên tới $10^5 \times 10^5 \times 2^{31}$), cần sử dụng long long (64-bit) để lưu trữ.
  • Sai lệch công thức: Đừng cố gắng tính trực tiếp phép cộng $(Ai \text{or} Aj) + (Ai \text{xor} Aj)$ rồi XOR với $Ai \text{and} Aj$. Hãy phân tích theo bit.
  • Quên dịch chuyển bit: Khi tính giá trị đóng góp, phải nhân với $2^k$ (tức là $1LL \ll k$).
  • Độ lớn input: Với $N=10^5$, giải pháp $O(N^2)$ sẽ chạy trong $10^{10}$ thao tác, chắc chắn bị timeout.

Bình luận

Please read the guidelines before commenting.


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