首先我们看到题目其实挺懵的。
对于(a1 + a2) ^ (a1 + a3) ^ ... ^ (an-1 + an),感觉除了暴力一点办法都没有。
其实我们可以看到。所有的括号外面其实都是异或符号。那么我们最后求的是一个异或的值。
那么[0 - 1e7]异或的值必然不会超过2e7。于是我们可以考虑按位求每个数对的贡献。
于是我们枚举 log2e7 约等于26
之后我们要怎么求a + b对于k位的贡献呢?
首先我们发现a + b是否对k位有贡献是取决于k-1位的数的。于是我们就可以对于每一位枚举先对数组进行一个模1 << (k+1)的操作
于是我们继续观察两个[0, 1<<k+1 - 1]相加什么时候 k位会有1的贡献?
于是我们就可以对取模后的数组进行sort,用双指针进行维护了。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 2e6 + 10; int n; int a[maxn], b[maxn]; bool cal(int L, int R) { ll res = 0; for (int i = n, l=1, r=1; i; i--) { while (l <= n && b[i]+b[l] < L) l++; ///找到第一个>=L的位置 while (r <= n && b[i]+b[r] <= R) r++; ///找到第一个大于R的位置 res += r-l - (i >= l && r > i); ///如果i在[L, R-1]那么要减去1,因为不能选自己 } return (res/2) & 1; ///除以2是因为我们在计算数对的时候重复计算了两次 (i, j) (j, i) } int solve(int k) { int lmt = 1 << (k+1); for (int i = 1; i <= n; ++ i) { b[i] = a[i] % lmt; } sort(b+1, b+1+n); return cal(1<<k, (1<<(k+1))-1) ^ cal((1<<(k+1)) + (1<<k), (1<<(k+2))-2); } int main() { scanf("%d", &n); for (int i = 1; i <= n; ++ i) { scanf("%d", &a[i]); } int ans = 0; for (int i = 0; i < 30; ++ i) { ans |= (solve(i) << i); } cout << ans << endl; return 0; }