C2. XOR-convenience (Hard Version)
This is the hard version of the problem. The difference between the versions is that in this version, $1 \le i \le n-1$. Note that a correct solution for the hard version is not necessarily a correct solution for the easy version.
Given a positive integer $n$. Find some permutation$^{\text{∗}}$ $p$ of length $n$ such that for every $i$ ($\style{color:red}{1 \le i \le n-1}$) there exists $j$ ($\style{color:red}{i \le j \le n}$) such that $p_i = p_j \oplus i$ $^{\text{†}}$, or determine that it does not exist.
$^{\text{∗}}$A permutation of length $n$ is an array consisting of $n$ distinct integers from $1$ to $n$ in arbitrary order. For example, $[2,3,1,5,4]$ is a permutation, but $[1,2,2]$ is not a permutation ($2$ appears twice in the array), and $[1,3,4]$ is also not a permutation ($n=3$ but there is $4$ in the array).
$^{\text{†}}$$\oplus$ denotes the bitwise XOR operation.
Input
Each test contains multiple test cases. The first line contains the number of test cases $t$ ($1 \le t \le 10^4$). The description of the test cases follows.
The only line of each test case contains one positive integer $n$ ($3 \leq n \leq 2 \cdot 10^5$) — the length of the permutation.
It is guaranteed that the sum of $n$ over all test cases does not exceed $2\cdot 10^5$.
Output
For each test case, if there is a suitable permutation, output $n$ integers $p_1,p_2,\ldots,p_n$ — the permutation $p$. Otherwise, output $-1$.
If multiple solutions exist, output any of them.
Example
Input
2
3
4
Output
2 1 3
-1
Note
In the first test case, the permutation $p = [2,1,3]$ fulfills the condition, as $p_2 = 1$ and $p_3 \oplus 2 = 1$.
In the second test case, there is no suitable permutation.
解题思路
纯电波题。虽然 Easy Version 与 Hard Version 是两道不同的题,但 Easy Version 的构造思路对 Hard Version 有很强的启发意义,因此先讲一下 Easy Version 的做法。
条件 $p_i = p_j \oplus i$ 等价于 $p_i \oplus i = p_j$。直觉上可能会猜:能否让每个 $p_i \oplus i \left(2 \leq i < n\right)$ 都等于同一个值,然后把这个值放在 $p_n$ 的位置上。于是会尝试把这个公共值设为 $1$ 或 $n$ 这样特殊值。然后就是惊人的注意力发力了,注意到 $2k \oplus (2k+1) = 1 \left(k \in \mathbb{N}\right)$,这意味着可以尝试将 $p_n$ 定为 $1$。
具体的构造方法是从自然排列 $p_i = i$ 开始,对于 $i = 2, 4, 6, \dots$ 这些偶数位置,向后和下一个位置交换,即把相邻两个数两两交换。这样处理后,对于 $i \in [2, n-1]$ 的位置,会有:
- 当 $i$ 为偶数时,有 $p_i = i+1$,$p_i \oplus i = (i+1) \oplus i = 1$。
- 当 $i$ 为奇数时,有 $p_i = i-1$,$p_i \oplus i = (i-1) \oplus i = 1$。
于是所有的 $p_i \oplus i \left(2 \leq n-1 \right)$ 都等于 $1$。此时 $p_1=1$,而 $p_n$ 的值根据 $n$ 的奇偶性分为:当 $n$ 为偶数时,$p_n = n$;当 $n$ 为奇数时,$p_n = n - 1$。最后再把 $p_1$ 和 $p_n$ 交换,就得到了 $p_n=1$。
然后是 Hard Version,需要额外处理 $i=1$ 的情况。当 $n$ 为奇数时,上述构造仍然是合法的。因为交换后 $p_1 = n-1$,而 $(n-1) \oplus 1 = n$,而根据前面的交换规律,$p_{n-1} = n$,刚好可以取 $j = n-1$。
当 $n$ 为偶数时。如果直接沿用上述构造方法,由于交换 $p_1$ 和 $p_n$ 后得到 $p_1 = n$,于是 $p_1 \oplus 1 = n \oplus 1 = n+1$,而 $n+1 > n$ 不存在于排列中,导致 $i=1$ 无解。然后依旧是惊人的注意力,当 $n$ 是 $2$ 的整数次幂,即 $n=2^k \left( k \in \mathbb{N} \right)$ 时,无解。可根据 $n$ 所在的位置来讨论:
- 当 $p_n = n$,则为了满足 $i=n-1$,需要 $p_{n-1} = n \oplus (n-1)$。但 $n \oplus (n-1) = 2n - 1 > n$,而 $p_{n-1} < n$,矛盾。
- 当 $p_i = n \left(1 \leq i < n\right)$,则 $p_i \oplus i = n + i > n$,而 $p_j < n \left(i < j \leq n\right)$,也找不到这样的值,矛盾。
接下来考虑 $n$ 为偶数但不是 $2$ 的次幂的情况,此时一定存在合法构造。令 $k = \lfloor \log_2 n \rfloor$,即 $n$ 在二进制下的最高位。$r = n \bmod 2^k$,即除最高位外其余位构成的值(由于 $n$ 不是 $2$ 的次幂,$r \ge 2$ 且为偶数)。这样有 $n = 2^k + r$。合法构造思路是:在 Easy Version 构造的基础上,再额外交换一次 $p_1$ 和 $p_r$。
具体来说,在得到 Easy Version 构造方案后,此时 $p_1 = n$,$p_r = r+1$。然后再把 $p_1$ 和 $p_r$ 交换,得到 $p_1 = r+1$,$p_r = n$。
- 对于 $i=1$,$p_1 \oplus 1 = (r+1) \oplus 1 = r < n$,由于 $p_{r+1} = r$,所以存在 $j = r+1$ 使得 $p_1 \oplus 1 = p_j$。
- 对于 $i=r$,$p_r \oplus r = n \oplus r = 2^k > r$,由于 $2^k + 1 < n$ 且 $p_{2^k + 1} = 2^k$,所以存在 $j = 2^k + 1$ 使得 $p_r \oplus r = p_j$。
其余的 $i \in [2,n-1]$(不包括 $i=r$)的位置仍然保持 $p_i \oplus i = 1$,而 $p_n = 1$,因此仍满足条件。
至于该构造方法是怎么想到的,评价是有惊人的注意力就行了。
AC 代码如下,时间复杂度为 $O(n)$:
#include <bits/stdc++.h>
using namespace std;typedef long long LL;const int N = 2e5 + 5;int p[N];void solve() {int n;cin >> n;iota(p + 1, p + n + 1, 1);if (!(n - 1 & n)) {cout << "-1\n";return;}for (int i = 2; i < n; i += 2) {swap(p[i], p[i + 1]);}swap(p[1], p[n]);if (~n & 1) swap(p[1], p[n % (1 << __lg(n))]);for (int i = 1; i <= n; i++) {cout << p[i] << ' ';}cout << '\n';
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);int t;cin >> t;while (t--) {solve();}return 0;
}
参考资料
Codeforces Round 1075 (Div. 2) Editorial:https://codeforces.com/blog/entry/150452