CF1322B Present(思维 + 位运算 + 双指针 + 枚举)

首先我们看到题目其实挺懵的。

对于(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;
}
原文地址:https://www.cnblogs.com/Vikyanite/p/15128262.html