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

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 !
评论
  目录