# **Phân tích và Giải pháp bài toán XOR (marisaoj.com/problem/688)**
### **1. Hướng tiếp cận**
> **Hướng tiếp cận:** (Giải thích thì dài, nhma code siêu gọn🐧)
>
> **Tóm gọn mục tiêu:** Tìm các cặp `(i, j)` sao cho: $$a_i \le (a_i \oplus a_j) \le a_j$$
>
> - Bài này đếm cặp => ta ưu tiên **sort** lại để đếm.
>
> **Lưu ý 2 tính chất quan trọng:**
> - $2^k > \sum_{p=0}^{k-1} 2^p$ (Bit lớn nhất quyết định độ lớn của số).
> - `1 ⊕ 1 = 0` (Phép XOR có thể "tắt" bit).
Sau khi sắp xếp, bài toán chuyển thành: Với mỗi `y = a[j]`, ta cần tìm số lượng `x = a[i]` (với `i ≤ j`) thỏa mãn $x \le (x \oplus y) \le y$.
---
### **2. Phân tích & Chứng minh**
Ta cần thỏa mãn đồng thời 2 điều kiện:
1. `(x ⊕ y) ≤ y`: Để kết quả không lớn hơn `y`, phép XOR phải "tắt" ít nhất một bit `1` của `y`. Điều này chỉ xảy ra khi `x` có chung một bit `1` nào đó với `y`.
2. `x ≤ (x ⊕ y)`: Đây là điều kiện khó hơn.
> **Chứng minh bằng ví dụ:**
>
> - **Trường hợp sai:** Nếu `MSB(x)` (bit 1 cao nhất của `x`) trùng với `MSB(y)`.
> - Ví dụ: `y = 169 (10101001)`, `x = 128 (10000000)`.
> - `MSB(x) = MSB(y) = 7`.
> - `x ⊕ y = 41 (00101001)`.
> - Ta thấy `41 < 128`, tức là `(x ⊕ y) < x`. **Không thỏa mãn.**
> - *Lý do:* Khi tắt đi bit quan trọng nhất, giá trị của số sẽ giảm mạnh, nhỏ hơn cả `x` ban đầu.
>
> - **Trường hợp đúng:** Để `x ≤ (x ⊕ y)`, `MSB(y)` phải được giữ nguyên trong phép XOR. Điều này có nghĩa là bit tại vị trí `MSB(y)` của `x` phải là `0` (luôn đúng khi `x < y`).
>
> **Kết hợp lại:**
>
> Để thỏa mãn cả 2 điều kiện, `x` phải "tắt" một bit nào đó của `y` nhưng không được đụng đến `MSB(y)`.
>
> => **Điều kiện cuối cùng:** `MSB(x)` phải trùng với một trong các bit `1` của `y`, **ngoại trừ** bit `MSB(y)`.
>
> Nói cách khác:
>
> > Với mỗi `y = a[j]`, ta chỉ cần tìm số lượng `x = a[i]` (`i < j`) sao cho `MSB(x)` trùng với một trong các bit từ **"vị trí 1 lớn nhì"** trở xuống của `y`.
---
### **3. Thuật toán & Code**
- Sắp xếp mảng `a`.
- Duyệt qua từng phần tử `y = a[i]`.
- Với mỗi `y`, ta duyệt qua các vị trí bit `k` của nó (từ bit 1 lớn nhì trở xuống).
- Nếu bit `k` của `y` đang bật, ta đếm xem đã có bao nhiêu số `x` đứng trước có `MSB(x) = k`.
- Dùng mảng `save[]` để lưu tần suất xuất hiện của các `MSB`. `save[k+1]` lưu số lượng số có `MSB` tại vị trí `k`.
> **CÁCH TÌM VỊ TRÍ BIT LỚN NHẤT CỦA 1 SỐ X:** `__lg(x)`
**Code tham khảo:**
```cpp
#include <bits/stdc++.h>
// Hàm tìm MSB, nếu x=0 thì trả về -1 để tiện xử lý
#define log2(x) ((x <= 0) ? -1 : __lg(x))
#define FOR(i, l, r, x) for(int i = l; i <= r; i += x)
#define FOD(i, l, r, x) for(int i = l; i >= r; i -= x)
#define pii pair<int, int>
#define fi first
#define se second
#define int long long
using namespace std;
const int N = 5e5 + 5;
const int mod = 1e9 + 7;
int n, a[N], save[N];
// Hàm tìm vị trí bit 1 lớn nhì
int bit2nd(int n) {
int inx = log2(n); // Tìm bit lớn nhất
if (inx == -1) return -1;
n ^= (1 << inx); // Tắt bit lớn nhất đi
return log2(n); // Tìm bit lớn nhất của số còn lại
}
// Kiểm tra bit thứ i của mask có bật không
bool isOn(int mask, int i) {
return ((mask >> i) & 1);
}
void solve() {
cin >> n;
FOR(i, 1, n, 1) {
cin >> a[i];
}
// Sắp xếp để đảm bảo khi xét a[i], các số đứng trước luôn nhỏ hơn hoặc bằng
sort(a + 1, a + n + 1);
int ans = 0;
// Duyệt qua mảng, coi a[i] là y (phần tử lớn hơn trong cặp)
FOR(i, 1, n, 1) {
int y = a[i];
// Tối ưu: chỉ cần xét các bit của y lên tới bit 1 lớn nhì của nó
int pos = bit2nd(y) + 1; /* Đoạn này +1 là do ta phải nhường
slot cho trường hợp y = 0 (hay a[i] = 0) (do log2(0) sẽ không xác định)*/
// Xử lý cặp (y, y). Nếu y=0, đây là cặp (0,0).
// Nếu y>0, đây là cặp (y,y) cũng luôn hợp lệ.
// Code gốc gộp lại: nếu y=0 thì ++ans, các cặp (0,0) sau sẽ được đếm ở save.
if (y == 0) {
++ans;
}
// Duyệt qua các khả năng cho MSB của x (phần tử nhỏ hơn).
// j là log2(x) + 1
FOR(j, 0, pos, 1) {
// Trường hợp x = 0. Cặp (0, y) luôn hợp lệ.
if (j == 0) {
ans += save[j]; // Cộng tất cả các số 0 đã gặp
continue;
}
// Đây là điều kiện cốt lõi từ chứng minh:
// Kiểm tra xem bit tại vị trí MSB của x (tức là j-1)
// có được bật trong y (tức là a[i]) hay không.
if (isOn(y, j - 1)) {
// Nếu có, tất cả các số x đã gặp có MSB tại vị trí đó đều tạo cặp hợp lệ.
ans += save[j];
}
}
// Cập nhật: "ghi nhớ" sự xuất hiện của y (a[i]) cho các vòng lặp sau.
// save[k+1] lưu số lượng số có MSB tại vị trí k.
save[log2(y) + 1] ++;
}
cout << ans << '\n';
}
signed main() {
#define name "task"
if (ifstream(name".inp")) {
freopen(name".inp", "r", stdin);
freopen(name".out", "w", stdout);
}
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solve();
}