CF1322B Present 题解
题目链接:CF1322B Present
题意:
给出一个长度为 \(n\) 的数列 \(a\)。其中第 \(i\) 项为 \(a_i\)。
请你求出 \(\bigoplus_{i=1}^{n}\bigoplus_{j=i+1}^{n}(a_i+a_j)\)。其中 \(\oplus\) 表示按位异或操作。
\(2 \leq n \leq 4 \times 10^5\),\(1 \leq a_i \leq 10^7\)。
输入格式:
The first line contains a single integer \(n\) — the number of integers in the array.
The second line contains integers \(a_1, a_2, \ldots, a_n\) .
输出格式:
Print a single integer — xor of all pairwise sums of integers in the given array.
注意到 \(a_i \le 10^7\) ,因此二进制的位数 \(\in [0, 25]\),于是依次考虑每一位的贡献。
显然,如果我们要计算两个数的和的第 \(i\) 位是否为 \(1\) ,至少需要这两个数前 \(i\) 位的信息
不妨预处理出每个数 \(a_j\) 在 \(i\) 相同时前 \(i\) 位所对应的数,记为 \(b_j\) 。
如果 \(b_x+b_y\) 的第 \(i\) 位为 \(1\) ,则
- 第 \(i\) 位没有进位,则 \(b_x + b_y \in [2^i,~2^{i+1}-1]\) 。
- 第 \(i\) 位进位了,但第 \(i-1\) 位也进位了,则 \(b_x + b_y \in [2^{i+1} + 2^i,~(2^{i+1}-1)\times 2]\) 。
那么知道这个范围有什么用处呢?
通常我们要数有多少个数在区间 \([l,r]\) 内,我们可以用 \([1,r]\) 的答案减去 \([1,l)\) 的答案。
于是,对于一个固定的 \(b_x\) ,我们只需要找到有多少个 \(b_y\) 满足 \(b_x + b_y\) 小于等于某个数
可以发现,当 \(b\) 内的每个数都从小到大排序时,我们倒序枚举 \(b_x\) ,那么 \(b_y\) 可以用一个指针来线性维护
不过最后我们要的答案是他们的异或值,不是个数,这个显然可以通过数目的奇偶性判断。
时间复杂度 \(\mathcal{O}(n \log V)\)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(4e5 + 15))
int n, res, a[N], b[N];
bool check(int x, int y)
{
if(x > y) return 0;
int num = 0;
for(int i = n, l = 1, r = 1; i; i--)
{
while(l <= n && b[i] + b[l] < x) ++l;
while(r <= n && b[i] + b[r] <= y) ++r;
num += r - l - (l <= i && i < r);
}
return (num >> 1) & 1;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 0; i <= 25; i++)
{
for(int j = 1; j <= n; j++) b[j] = a[j] & ((1 << (i + 1)) - 1);
sort(b + 1, b + 1 + n);
int tmp = check(1 << i, (1 << (i + 1)) - 1) ^ check(3 << i, (1 << (i + 2)) - 2);
res += tmp * (1 << i);
}
cout << res << '\n';
return 0;
}
参考文献: