嘘~ 正在从服务器偷取页面 . . .

CF1322B Present 题解


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;
}

参考文献

[1] https://www.luogu.com.cn/article/dn6gk2ge


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录