原题链接
给定一个长度为 n n n 的整数数组 a a a ,一个长度为 n n n 的整数数组 b b b 。
问所有 a i + b j a_i+ b_j ai+bj 的异或和是多少。
1 ≤ n ≤ 200000 1\leq n\leq 200000 1≤n≤200000
0 ≤ a i , b i < 2 28 0\leq a_i,b_i <2^{28} 0≤ai,bi<228
考虑二进制第 k k k 位,二进制位从第 0 0 0 位开始。由于这里会产生进位,我们考虑什么情况下第 k k k 位为 1 1 1 ,即 n 2 n^2 n2 个加法结果的第 k k k 位有奇数个 1 1 1 。
对于第 k k k 位的结果,其必然只和前 k k k 位的加法有关。
考虑 a b i t = a i m o d 2 k + 1 , b b i t = b j m o d 2 k + 1 a_{bit}=a_i\bmod 2^{k+1}, b_{bit}=b_j\bmod 2^{k+1} abit=aimod2k+1,bbit=bjmod2k+1 。
那么 a b i t + b b i t a_{bit}+b_{bit} abit+bbit 的结果的第 k k k 位为 1 1 1 有哪些情况呢
以 k = 2 k=2 k=2 为例:
和在这两个区间内的一对数,其第 k k k 位为 1 1 1 。
所以我们考虑枚举二进制位,然后枚举 a i a_i ai ,二分答案 b j b_j bj 即可,对于 b b b 需要二分,所以就得排序,排序按照取模后的值进行排序。
时间复杂度: O ( 29 n log n ) O(29n\log n) O(29nlogn)
考虑二分是有序的,我们可以对 a a a 也进行排序。
然后进行两次双指针,一个用来考虑第一个区间 [ 2 k , 2 k + 1 − 1 ] [2^k,2^{k+1}-1] [2k,2k+1−1] ,另一个用来考虑第二个区间 [ 3 ⋅ 2 k , 2 k + 2 − 1 ] [3\cdot 2^k,2^{k+2}-1] [3⋅2k,2k+2−1] 。
但是由于排序的限制,时间复杂度不变。
时间复杂度: O ( 29 n log n ) O(29n\log n) O(29nlogn)
现在瓶颈在于排序,一般来说,涉及到的排序问题是无法进行优化的。
但是这里不同,这里我们一个条件是需要枚举二进制位,并且从小到大枚举。
那么就是天然适配基数排序的,在这里,基数排序的复杂度被均摊到了每一个二进制位枚举中。
所以优化后时间复杂度为: O ( 29 n ) O(29n) O(29n)
#include
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n), b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
auto bs1 = [&](int ai, int p, int x) {
int l = 0, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if ((ai & p) + (b[mid] & p) >= x) r = mid;
else l = mid + 1;
}
return l;
};
auto bs2 = [&](int ai, int p, int x) {
int l = 0, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if ((ai & p) + (b[mid] & p) > x) r = mid;
else l = mid + 1;
}
return l;
};
auto count = [&](int p, int minv, int maxv) {
// 01000 ~ 01111
int c = 0;
for (int i = 0; i < n; ++i) {
int L = bs1(a[i], p, minv);
if (L != n) {
int R = bs2(a[i], p, maxv);
if ((R - L) & 1) c += 1;
}
}
return c & 1;
};
int ans = 0;
for (int j = 1; j <= 30; ++j) {
int mod = (1 << j) - 1;
sort(b.begin(), b.end(), [&](const int x, const int y) {
return (x & mod) < (y & mod);
});
// minv = 01000... ~ 01111...
int minv = 1 << (j - 1);
int maxv = (1 << j) - 1;
int cnt = count(mod, minv, maxv);
// minv = 11000... ~ 11111...
minv = (1 << (j - 1))+ (1 << j);
maxv = (1 << (j + 1)) - 1;
cnt += count(mod, minv, maxv);
if (cnt & 1) {
ans |= 1 << (j - 1);
}
}
cout << ans << "\n";
return 0;
}
#include
using namespace std;
const int MAX = 1000000;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n);
vector<int> b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
int ans = 0;
for (int k = 0; k < 29; ++k) {
int mask = (1 << (k + 1)) - 1;
sort(a.begin(), a.end(), [&](const int x, const int y) { return (x & mask) < (y & mask); });
sort(b.begin(), b.end(), [&](const int x, const int y) { return (x & mask) < (y & mask); });
int l1 = n - 1, r1 = n - 1, l2 = n - 1, r2 = n - 1;
int p = 1 << k, q = (1 << (k + 1)) - 1, r = 3 << k, s = (1 << (k + 2)) - 1;
int cnt = 0;
for (int i = 0; i < n; ++i) {
while (l1 >= 0 && ((a[i] & mask) + (b[l1] & mask)) >= p) l1 -= 1;
while (r1 >= 0 && ((a[i] & mask) + (b[r1] & mask)) > q) r1 -= 1;
while (l2 >= 0 && ((a[i] & mask) + (b[l2] & mask)) >= r) l2 -= 1;
while (r2 >= 0 && ((a[i] & mask) + (b[r2] & mask)) > s) r2 -= 1;
cnt = (cnt + (r1 - l1) + (r2 - l2)) & 1;
}
if (cnt) ans |= 1 << k;
}
cout << ans << "\n";
return 0;
}
#include
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<int> a(n), b(n);
for (int i = 0; i < n; ++i) cin >> a[i];
for (int i = 0; i < n; ++i) cin >> b[i];
int ans = 0;
vector<vector<int>> nums(2, vector<int>(n));
auto mySort = [&](int k, vector<int>& vec) {
int c[2] = {-1, -1};
for (auto u: vec) {
int bit = u >> k & 1;
nums[bit][++c[bit]] = u;
}
for (int i = 0, j = 0; i < 2; ++i) {
for (int t = 0; t <= c[i]; ++t) {
vec[j] = nums[i][t];
j++;
}
}
};
for (int k = 0; k < 29; ++k) {
int mask = (1 << (k + 1)) - 1;
// sort 转换为基数排序
mySort(k, a);
mySort(k, b);
int cnt = 0;
int i = n - 1, j = n - 1, p = n - 1, q = n - 1;
int l1 = 1 << k, r1 = (1 << (k + 1)) - 1, l2 = 3 << k, r2 = (1 << (k + 2)) - 1;
for (int ai: a) {
while (i >= 0 && (ai & mask) + (b[i] & mask) >= l1) i--;
while (j >= 0 && (ai & mask) + (b[j] & mask) > r1) j--;
while (p >= 0 && (ai & mask) + (b[p] & mask) >= l2) p--;
while (q >= 0 && (ai & mask) + (b[q] & mask) > r2) r2 -= 1;
cnt += (j - i) + (n - 1 - p);
}
if (cnt & 1) ans |= 1 << k;
}
cout << ans << "\n";
return 0;
}