D. XOR Construction

发布时间 2023-11-05 16:52:48作者: onlyblues

D. XOR Construction

You are given $n-1$ integers $a_1, a_2, \dots, a_{n-1}$.

Your task is to construct an array $b_1, b_2, \dots, b_n$ such that:

  • every integer from $0$ to $n-1$ appears in $b$ exactly once;
  • for every $i$ from $1$ to $n-1$, $b_i \oplus b_{i+1} = a_i$ (where $\oplus$ denotes the bitwise XOR operator).

Input

The first line contains one integer $n$ ($2 \le n \le 2 \cdot 10^5$).

The second line contains $n-1$ integers $a_1, a_2, \dots, a_{n-1}$ ($0 \le a_i \le 2n$).

Additional constraint on the input: it's always possible to construct at least one valid array $b$ from the given sequence $a$.

Output

Print $n$ integers $b_1, b_2, \dots, b_n$. If there are multiple such arrays, you may print any of them.

Examples

input

4
2 1 2

output

0 2 3 1 

input

6
1 6 1 4 1

output

2 3 5 4 0 1 

 

解题思路

  我的做法跟官方的基本一样。最关键的地方是要观察出 $a$ 其实就是 $b$ 的差分数组,假设下标都从 $0$ 开始,即有 $b_i = b_0 \oplus a_0 \oplus a_1 \oplus \cdots \oplus a_{i-1}$。主要是如果看到有 $a_i = b_i - b_{i-1}$ 或者 $a_i = b_i \oplus b_{i-1}$ 就要想到对 $a$ 求前缀和就会得到 $b$。

  然而我们现在还不知到 $b_0$,假设 $c_0 = 0$,$c_i = a_0 \oplus \cdots \oplus a_{i-1}, \, (i \geq 1)$,如果知道 $b_0$ 的话那么整个 $b$ 数组都确定下来了,就是 $b_0$ 异或每个 $c_i$。所以很自然想到枚举 $0 \sim n-1$ 来找到合法 $b_0$,使得 $b_0$ 与每个 $b_0 \oplus c_i$ 恰好是 $0 \sim n-1$ 的全排列。如果直接暴力验证的话时间复杂度就是 $O(n^2)$,注意到题目保证存在解,所以可以分析一下 $c_i$ 具有什么性质。

  首先 $c_i$ 一定是成对不同的,否则假设 $c_i = c_j$,那么不管 $b_0$ 取什么必然有 $c_i \oplus b_0 = c_j \oplus b_0$,因此不可能存在解,矛盾。由于 $c_i$ 成对不同的,意味着 $c_i \oplus b_0$ 也是成对不同的,所以如果 $\max\limits_{0 \leq i \leq n-1}\{ c_i \oplus b_0 \} < n$,那么此时 $c_i \oplus b_0$ 一定是 $0 \sim n-1$ 的全排列,此时的 $b_0$ 就是合法的解。

  为了快速得到 $b_0$ 与这些 $c_i$ 异或后的最大值是多少,可以将 $c_i$ 存到 trie 中,然后从最高位开始贪心选择即可。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;

int a[N];
int tr[N * 19][2], idx;

void add(int x) {
    int p = 0;
    for (int i = 18; i >= 0; i--) {
        int t = x >> i & 1;
        if (!tr[p][t]) tr[p][t] = ++idx;
        p = tr[p][t];
    }
}

int query(int x) {
    int p = 0, ret = 0;
    for (int i = 18; i >= 0; i--) {
        int t = x >> i & 1;
        if (tr[p][!t]) {
            p = tr[p][!t];
            ret |= 1 << i;
        }
        else {
            p = tr[p][t];
        }
    }
    return ret;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int x;
        scanf("%d", &x);
        a[i] = a[i - 1] ^ x;
        add(a[i]);
    }
    add(0);
    for (int i = 0; i < n; i++) {
        if (query(i) < n) {
            for (int j = 0; j < n; j++) {
                printf("%d ", a[j] ^ i);
            }
            break;
        }
    }
    
    return 0;
}

  再提供另外一种思路,按位考虑每一个 $c_i$。对于二进制的第 $k$ 位,如果每个 $c_i$ 的第 $k$ 位共有 $x$ 个 $1$,数字 $0 \sim n-1$ 的第 $k$ 位共有 $y$ 个 $1$,由于题目保证有解,因此如果 $x \ne y$,那么只需让 $b_0$ 的第 $k$ 位为 $1$ 即可,否则为 $0$。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 2e5 + 10;

int a[N];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        int x;
        scanf("%d", &x);
        a[i] = a[i - 1] ^ x;
    }
    int ret = 0;
    for (int i = 0; i <= 18; i++) {
        int s = 0;
        for (int j = 0; j < n; j++) {
            s += j >> i & 1;
            s -= a[j] >> i & 1;
        }
        if (s) ret |= 1 << i;
    }
    for (int i = 0; i < n; i++) {
        printf("%d ", a[i] ^ ret);
    }
    
    return 0;
}

 

参考资料

  Educational Codeforces Round 157 Editorial:https://codeforces.com/blog/entry/122034

  Educational Codeforces Round 157 (Rated for Div. 2):https://zhuanlan.zhihu.com/p/665022975